11import Foundation
22import GraphViz
3+ import UseGraphCore
34import Utils
45
5- protocol CSVRepresentable {
6- var csvRepresentation : String { get }
7- var fields : [ String ] { get }
8- }
9-
10- struct EdgeCSV : CSVRepresentable {
11- var fields : [ String ] {
12- [ " Source " , " Target " , " Type " ]
13- }
14-
15- let source : String
16- let target : String
17- let type = " directed "
18-
19- var csvRepresentation : String {
20- source + " , " + target + " , " + type
21- }
22- }
23-
246enum OutputFormat {
257 case svg
268 case png
279 case gv
28-
10+
2911 public static func parse( format: String ) throws -> OutputFormat {
3012 switch format. lowercased ( ) {
3113 case " svg " :
32- . svg
14+ . svg
3315 case " png " :
34- . png
16+ . png
3517 case " gv " :
36- . gv
18+ . gv
3719 default :
3820 throw FormatError . formatIsNotCorrect
3921 }
@@ -42,100 +24,74 @@ enum OutputFormat {
4224
4325final class GraphBuilder {
4426 static let shared = GraphBuilder ( )
45-
46- private init ( ) { }
47-
48- private func createCSV( from recArray: [ CSVRepresentable ] ) -> String {
49- guard let fields = recArray. first? . fields else { return " " }
50- var csvString = fields. joined ( separator: " , " ) + " \n "
51- for dct in recArray {
52- csvString = csvString. appending ( dct. csvRepresentation + " \n " )
53- }
54- return csvString
27+ let csvBuilder : CSVBuilding
28+ let outputGraphBuilder : OutputGraphBuilding
29+
30+ private init (
31+ csvBuilder: CSVBuilding = CSVBuilder ( ) ,
32+ outputGraphBuilder: OutputGraphBuilding = OutputGraphBuilder ( )
33+ ) {
34+ self . csvBuilder = csvBuilder
35+ self . outputGraphBuilder = outputGraphBuilder
5536 }
56-
37+
5738 func csvBuildGraph( edges: [ UseGraphPeriphery . Edge ] ) {
58- var uniqueSet = Set < Node > ( )
39+ var uniqueSet = Set < UseGraphCore . Node > ( )
5940 edges. map { [ $0. from, $0. to] } . flatMap { $0 } . forEach { uniqueSet. insert ( $0) }
60-
61- let edges = edges. map { EdgeCSV ( source: $0. from. id, target: $0. to. id) }
62- let edgesCSV = createCSV ( from: edges)
63- let nodesCSV = createCSV ( from: Array ( uniqueSet) )
64-
41+
42+ let edges = edges. map { UseGraphCore . Edge ( source: $0. from. id, target: $0. to. id) }
43+ let edgesCSV = csvBuilder . createCSV ( from: edges)
44+ let nodesCSV = csvBuilder . createCSV ( from: Array ( uniqueSet) )
45+
6546 let nodesUrl = URL ( fileURLWithPath: #file) . deletingLastPathComponent ( ) . appending ( path: " Nodes.csv " )
6647 let edgesUrl = URL ( fileURLWithPath: #file) . deletingLastPathComponent ( ) . appending ( path: " Edges.csv " )
67-
48+
6849 guard let edgesData = edgesCSV. data ( using: . utf8) ,
6950 let nodesData = nodesCSV. data ( using: . utf8) else { fatalError ( ) }
7051 print ( FileManager . default. createFile ( atPath: edgesUrl. path ( ) , contents: edgesData) )
7152 print ( FileManager . default. createFile ( atPath: nodesUrl. path ( ) , contents: nodesData) )
7253 }
73-
54+
7455 func buildGraph( edges: [ Edge ] , format: OutputFormat ) async throws {
7556 switch format {
7657 case . svg, . png, . gv:
7758 guard let format = mapFormat ( format: format) else { fatalError ( ) }
78-
7959 let data = try await buildGraphData ( edges: edges, format: format)
8060 let url = URL ( fileURLWithPath: #file) . deletingLastPathComponent ( ) . appending ( path: " Graph. \( format. rawValue) " )
81- guard var fileContents = String ( data: data, encoding: . utf8) else { fatalError ( ) }
82- if format == . gv {
83- fileContents = removeSecondAndThirdLine ( string: fileContents)
84- }
85-
61+ guard let fileContents = String ( data: data, encoding: . utf8) else { fatalError ( ) }
62+
8663 print ( FileManager . default. createFile ( atPath: url. path ( ) , contents: fileContents. data ( using: . utf8) ) )
8764 Task {
8865 System . shared. run ( " open \( url. path ( ) ) " )
8966 }
9067 }
9168 }
92-
69+
9370 func buildGraphData( edges: [ Edge ] , format: Format ) async throws -> Data {
9471 var graph = Graph ( directed: true )
95-
72+
9673 for edge in edges {
9774 graph. append (
98- GraphViz . Edge (
99- from: GraphViz . Node ( edge. from. id) ,
100- to: GraphViz . Node ( edge. to. id)
101- )
75+ GraphViz . Edge (
76+ from: GraphViz . Node ( edge. from. id) ,
77+ to: GraphViz . Node ( edge. to. id)
78+ )
10279 )
10380 }
104-
105- graph. render ( using: . circo, to: . svg, completion: { _ in } )
106-
107- print ( " Start building graph... " )
108-
109- return try await withCheckedThrowingContinuation { continuation in
110- graph. render ( using: . fdp, to: format) { [ weak self] result in
111- guard self != nil else { return }
112- switch result {
113- case let . success( data) :
114- continuation. resume ( returning: data)
115- case let . failure( failure) :
116- continuation. resume ( throwing: BuildGraphError . buildGraphError)
117- print ( failure)
118- }
119- }
120- }
121- }
122-
123- private func removeSecondAndThirdLine( string: String ) -> String {
124- var lines = string. split ( separator: " \n " )
125- lines. removeSubrange ( 1 ... 2 )
126- return lines. joined ( separator: " \n " )
81+
82+ return try await outputGraphBuilder. buildGraphData ( graph: graph, format: format)
12783 }
12884}
12985
13086extension GraphBuilder {
13187 func mapFormat( format: OutputFormat ) -> Format ? {
13288 switch format {
13389 case . svg:
134- . svg
90+ . svg
13591 case . png:
136- . png
92+ . png
13793 case . gv:
138- . gv
94+ . gv
13995 }
14096 }
14197}
0 commit comments