@@ -12,32 +12,92 @@ import SymbolKit
12
12
13
13
@main
14
14
struct SnippetExtractCommand {
15
- var snippetsDir : String
16
- var outputDir : String
15
+ enum OptionName : String {
16
+ case moduleName = " --module-name "
17
+ case outputFile = " --output "
18
+ }
19
+
20
+ enum Argument {
21
+ case moduleName( String )
22
+ case outputFile( String )
23
+ case inputFile( String )
24
+ }
25
+
26
+ enum ArgumentError : Error , CustomStringConvertible {
27
+ case missingOption( OptionName )
28
+ case missingOptionValue( OptionName )
29
+ case snippetNotContainedInSnippetsDirectory( URL )
30
+
31
+ var description : String {
32
+ switch self {
33
+ case . missingOption( let optionName) :
34
+ return " Missing required option \( optionName. rawValue) "
35
+ case . missingOptionValue( let optionName) :
36
+ return " Missing required option value for \( optionName. rawValue) "
37
+ case . snippetNotContainedInSnippetsDirectory( let snippetFileURL) :
38
+ return " Snippet file ' \( snippetFileURL. path) ' is not contained in a directory called 'Snippets' at any level, so this tool is not able to compute the path components that would be used for linking to the snippet. It may exist in a subdirectory, but one of its parent directories must be named 'Snippets'. "
39
+ }
40
+ }
41
+ }
42
+
43
+ var snippetFiles = [ String] ( )
44
+ var outputFile : String
17
45
var moduleName : String
18
46
19
47
static func printUsage( ) {
20
48
let usage = """
21
- USAGE: snippet-extract <snippet directory> <output directory> <module name>
49
+ USAGE: snippet-extract --output <output file> --module-name <module name> <input files >
22
50
23
51
ARGUMENTS:
24
- <snippet directory> - The directory containing Swift snippets
25
- <output directory> - The diretory in which to place Symbol Graph JSON file(s) representing the snippets
26
- <module name> - The module name to use for the Symbol Graph (typically should be the package name)
52
+ <output file> (Required)
53
+ The path of the output Symbol Graph JSON file representing the snippets for the a module or package
54
+ <module name> (Required)
55
+ The module name to use for the Symbol Graph (typically should be the package name)
56
+ <input files>
57
+ One or more absolute paths to snippet files to interpret as snippets
27
58
"""
28
59
print ( usage)
29
60
}
30
61
62
+ init ( arguments: [ String ] ) throws {
63
+ var arguments = arguments
64
+
65
+ var parsedOutputFile : String ? = nil
66
+ var parsedModuleName : String ? = nil
67
+
68
+ while let argument = try arguments. parseSnippetArgument ( ) {
69
+ switch argument {
70
+ case . inputFile( let inputFile) :
71
+ snippetFiles. append ( inputFile)
72
+ case . moduleName( let moduleName) :
73
+ parsedModuleName = moduleName
74
+ case . outputFile( let outputFile) :
75
+ parsedOutputFile = outputFile
76
+ }
77
+ }
78
+
79
+ guard let parsedOutputFile else {
80
+ throw ArgumentError . missingOption ( . outputFile)
81
+ }
82
+ self . outputFile = parsedOutputFile
83
+
84
+ guard let parsedModuleName else {
85
+ throw ArgumentError . missingOption ( . moduleName)
86
+ }
87
+ self . moduleName = parsedModuleName
88
+ }
89
+
31
90
func run( ) throws {
32
- let snippets = try loadSnippets ( from: URL ( fileURLWithPath: snippetsDir) )
91
+ let snippets = try snippetFiles. map {
92
+ try Snippet ( parsing: URL ( fileURLWithPath: $0) )
93
+ }
33
94
guard snippets. count > 0 else { return }
34
- let symbolGraphFilename = URL ( fileURLWithPath: outputDir)
35
- . appendingPathComponent ( " \( moduleName) -snippets.symbols.json " )
95
+ let symbolGraphFilename = URL ( fileURLWithPath: outputFile)
36
96
try emitSymbolGraph ( for: snippets, to: symbolGraphFilename, moduleName: moduleName)
37
97
}
38
98
39
99
func emitSymbolGraph( for snippets: [ Snippet ] , to emitFilename: URL , moduleName: String ) throws {
40
- let snippetSymbols = snippets. map { SymbolGraph . Symbol ( $0, moduleName: moduleName, inDirectory : URL ( fileURLWithPath : snippetsDir ) . absoluteURL ) }
100
+ let snippetSymbols = try snippets. map { try SymbolGraph . Symbol ( $0, moduleName: moduleName) }
41
101
let metadata = SymbolGraph . Metadata ( formatVersion: . init( major: 0 , minor: 1 , patch: 0 ) , generator: " snippet-extract " )
42
102
let module = SymbolGraph . Module ( name: moduleName, platform: . init( architecture: nil , vendor: nil , operatingSystem: nil , environment: nil ) , isVirtual: true )
43
103
let symbolGraph = SymbolGraph ( metadata: metadata, module: module, symbols: snippetSymbols, relationships: [ ] )
@@ -72,28 +132,42 @@ struct SnippetExtractCommand {
72
132
. filter { $0. isDirectory }
73
133
}
74
134
75
- func loadSnippets( from snippetsDirectory: URL ) throws -> [ Snippet ] {
76
- guard snippetsDirectory. isDirectory else {
77
- return [ ]
78
- }
79
-
80
- let snippetFiles = try files ( in: snippetsDirectory, withExtension: " swift " ) +
81
- subdirectories( in: snippetsDirectory)
82
- . flatMap { subdirectory -> [ URL ] in
83
- try files ( in: subdirectory, withExtension: " swift " )
84
- }
85
-
86
- return try snippetFiles. map { try Snippet ( parsing: $0) }
87
- }
88
-
89
135
static func main( ) throws {
90
- if CommandLine . arguments. count < 4 {
136
+ if CommandLine . arguments. count == 1 || CommandLine . arguments . contains ( " -h " ) || CommandLine . arguments . contains ( " --help " ) {
91
137
printUsage ( )
92
138
exit ( 0 )
93
139
}
94
- let snippetExtract = SnippetExtractCommand ( snippetsDir: CommandLine . arguments [ 1 ] ,
95
- outputDir: CommandLine . arguments [ 2 ] ,
96
- moduleName: CommandLine . arguments [ 3 ] )
97
- try snippetExtract. run ( )
140
+ do {
141
+ let snippetExtract = try SnippetExtractCommand ( arguments: Array ( CommandLine . arguments. dropFirst ( 1 ) ) )
142
+ try snippetExtract. run ( )
143
+ } catch let error as ArgumentError {
144
+ printUsage ( )
145
+ throw error
146
+ }
147
+ }
148
+ }
149
+
150
+ extension Array where Element == String {
151
+ mutating func parseSnippetArgument( ) throws -> SnippetExtractCommand . Argument ? {
152
+ guard let thisArgument = first else {
153
+ return nil
154
+ }
155
+ removeFirst ( )
156
+ switch thisArgument {
157
+ case " --module-name " :
158
+ guard let nextArgument = first else {
159
+ throw SnippetExtractCommand . ArgumentError. missingOptionValue ( . moduleName)
160
+ }
161
+ removeFirst ( )
162
+ return . moduleName( nextArgument)
163
+ case " --output " :
164
+ guard let nextArgument = first else {
165
+ throw SnippetExtractCommand . ArgumentError. missingOptionValue ( . outputFile)
166
+ }
167
+ removeFirst ( )
168
+ return . outputFile( nextArgument)
169
+ default :
170
+ return . inputFile( thisArgument)
171
+ }
98
172
}
99
173
}
0 commit comments