Stream: allow edge attributes, autoclose nodes

This commit is contained in:
Timo Hocker 2020-06-05 11:46:58 +02:00
parent 920dc74d9c
commit b11b2f5f2b
5 changed files with 61 additions and 26 deletions

16
CHANGELOG.md Normal file
View File

@ -0,0 +1,16 @@
# Changelog
## 1.2.0
Stream:
- automatically end node instructions
- allow attributes on edges
## 1.1.0
Allow creating graphs using a stream
## 1.0.0
initial version

2
Jenkinsfile vendored
View File

@ -5,7 +5,7 @@ pipeline {
VERSION = VersionNumber([ VERSION = VersionNumber([
versionNumberString: versionNumberString:
'${BUILDS_ALL_TIME}', '${BUILDS_ALL_TIME}',
versionPrefix: '1.1.', versionPrefix: '1.2.',
worstResultForIncrement: 'SUCCESS' worstResultForIncrement: 'SUCCESS'
]) ])
} }

View File

@ -1,6 +1,6 @@
# @sapphirecode/graphviz-builder # @sapphirecode/graphviz-builder
version: 1.1.x version: 1.2.x
constructing graphviz files using an easy typescript interface constructing graphviz files using an easy typescript interface

View File

@ -22,7 +22,11 @@ interface Stringable {
export class GraphStream extends Transform { export class GraphStream extends Transform {
private _path: string[] = []; private _path: string[] = [];
private _state: GraphStreamCommand | '' = ''; private _state: (GraphStreamCommand | '')[] = [
'',
''
];
private _directional = false; private _directional = false;
public get path (): string { public get path (): string {
@ -33,6 +37,16 @@ export class GraphStream extends Transform {
return ' '.repeat (this._path.length); return ' '.repeat (this._path.length);
} }
private finish_node ():void {
if (
[
'ce',
'cn'
].includes (this._state[1])
)
this.push ('\n');
}
private expect_state (instr: GraphStreamCommand): void { private expect_state (instr: GraphStreamCommand): void {
const states: (GraphStreamCommand|'')[] = []; const states: (GraphStreamCommand|'')[] = [];
if ([ if ([
@ -47,24 +61,21 @@ export class GraphStream extends Transform {
'csg', 'csg',
'ce' 'ce'
].includes (instr)) ].includes (instr))
states.push ('en', 'at', 'eg', 'cug', 'cdg', 'csg', 'ce'); states.push ('at', 'eg', 'cug', 'cdg', 'csg', 'ce', 'cn');
switch (instr) { switch (instr) {
case 'en':
states.push ('cn', 'at');
break;
case 'at': case 'at':
states.push ('cn', 'cug', 'cdg', 'csg'); states.push ('cn', 'cug', 'cdg', 'csg', 'ce');
break; break;
default: default:
break; break;
} }
if (!states.includes (this._state)) { if (!states.includes (this._state[1])) {
throw new Error (`invalid state to execute command ${ throw new Error (`invalid state to execute command ${
translate_command (instr)} translate_command (instr)}
expected: ${states.map ((s) => translate_command (s)) expected: ${states.map ((s) => translate_command (s))
.join (', ')} .join (', ')}
actual: ${translate_command (this._state)}`); actual: ${translate_command (this._state[1])}`);
} }
} }
@ -89,24 +100,27 @@ export class GraphStream extends Transform {
this._directional = true; this._directional = true;
break; break;
case 'csg': // create subgraph case 'csg': // create subgraph
this.finish_node ();
this.push ( this.push (
`${this.level}subgraph cluster_${this.path}_${instr.args[0]} {\n` `${this.level}subgraph cluster_${this.path}_${instr.args[0]} {\n`
); );
this._path.push (instr.args[0]); this._path.push (instr.args[0]);
break; break;
case 'eg': // end graph case 'eg': // end graph
this.finish_node ();
this._path.pop (); this._path.pop ();
this.push (`${this.level}}\n\n`); this.push (`${this.level}}\n\n`);
break; break;
case 'cn': // create node case 'cn': // create node
this.finish_node ();
this.push (`${this.level}${this.path}_${instr.args[0]}`); this.push (`${this.level}${this.path}_${instr.args[0]}`);
break; break;
case 'en': // end node
this.push ('\n');
break;
case 'at': // add attributes case 'at': // add attributes
if (this._state === 'cn') { if ([
this.push (` [${instr.args.join (', ')}]`); 'cn',
'ce'
].includes (this._state[1])) {
this.push (` [${instr.args.join (', ')}]\n`);
} }
else { else {
this.push (`${this.level}${ this.push (`${this.level}${
@ -116,14 +130,16 @@ export class GraphStream extends Transform {
} }
break; break;
case 'ce': case 'ce':
this.finish_node ();
this.push (`${this.level}${ this.push (`${this.level}${
instr.args[0] instr.args[0]
} -${this._directional ? '>' : '-'} ${instr.args[1]}\n`); } -${this._directional ? '>' : '-'} ${instr.args[1]}`);
break; break;
default: default:
break; break;
} }
this._state = instr.type; this._state.push (instr.type);
this._state.shift ();
callback (); callback ();
} }
@ -139,8 +155,11 @@ export class GraphStream extends Transform {
return `${this.path}_${node_name}`; return `${this.path}_${node_name}`;
} }
/**
* @deprecated calling this method is not needed anymore
*/
public end_node (): void { public end_node (): void {
this.write ({ type: 'en', args: [] }); // this.write ({ type: 'en', args: [] });
} }
public create_graph (name: string, type: 'u'|'d'|'s' = 's'): void { public create_graph (name: string, type: 'u'|'d'|'s' = 's'): void {

View File

@ -2,7 +2,7 @@
* Copyright (C) Sapphirecode - All Rights Reserved * Copyright (C) Sapphirecode - All Rights Reserved
* This file is part of graphviz-builder which is released under MIT. * This file is part of graphviz-builder which is released under MIT.
* See file 'LICENSE' for full license details. * See file 'LICENSE' for full license details.
* Created by Timo Hocker <timo@scode.ovh>, May 2020 * Created by Timo Hocker <timo@scode.ovh>, June 2020
*/ */
import test from 'ava'; import test from 'ava';
@ -37,6 +37,7 @@ const complex = `digraph foo {
foo_baz [label = "baz"] foo_baz [label = "baz"]
foo_foo [label = "foo"] foo_foo [label = "foo"]
foo_foo -> foo_baz foo_foo -> foo_baz
foo_foo -> foo_baz_asd [label = "edge attr"]
} }
`; `;
@ -76,20 +77,19 @@ test ('complex stream graph', (t) => new Promise ((resolve) => {
stream.attributes ({ color: Color.gray, style: 'dotted' }); stream.attributes ({ color: Color.gray, style: 'dotted' });
stream.end_graph (); stream.end_graph ();
stream.end_graph (); stream.end_graph ();
stream.create_node ('asd'); const asd = stream.create_node ('asd');
stream.attributes ({ label: 'asd' }); stream.attributes ({ label: 'asd' });
stream.end_node (); stream.end_node ();
stream.create_node ('test'); stream.create_node ('test');
stream.attributes ({ style: 'bold', color: Color.gray }); stream.attributes ({ style: 'bold', color: Color.gray });
stream.end_node ();
stream.end_graph (); stream.end_graph ();
stream.create_node ('baz'); const baz = stream.create_node ('baz');
stream.attributes ({ label: 'baz' }); stream.attributes ({ label: 'baz' });
stream.end_node (); const foo = stream.create_node ('foo');
stream.create_node ('foo');
stream.attributes ({ label: 'foo' }); stream.attributes ({ label: 'foo' });
stream.end_node (); stream.create_edge (foo, baz);
stream.create_edge (`${stream.path}_foo`, `${stream.path}_baz`); stream.create_edge (foo, asd);
stream.attributes ({ label: 'edge attr' });
stream.end_graph (); stream.end_graph ();
stream.end (); stream.end ();
})); }));