@@ -4,27 +4,36 @@ open Nap.Core
44
55/// Parse CLI arguments into a structured form
66type CliArgs = {
7- Command : string // "run", "check", "help"
8- File : string option
9- Env : string option
10- Vars : Map < string , string >
11- Output : string // "pretty", "junit", "json", "ndjson"
12- Verbose : bool
7+ Command : string // "run", "check", "generate", "help"
8+ SubCommand : string option // e.g. "openapi" for "generate openapi"
9+ File : string option
10+ Env : string option
11+ Vars : Map < string , string >
12+ Output : string // "pretty", "junit", "json", "ndjson"
13+ OutputDir : string option // --output-dir for generate command
14+ Verbose : bool
1315}
1416
1517let parseArgs ( argv : string array ) : CliArgs =
1618 let mutable command = " help"
19+ let mutable subCommand = None
1720 let mutable file = None
1821 let mutable env = None
1922 let mutable vars = Map.empty
2023 let mutable output = " pretty"
24+ let mutable outputDir = None
2125 let mutable verbose = false
2226 let mutable i = 0
2327
2428 if argv.Length > 0 then
2529 command <- argv[ 0 ]
2630 i <- 1
2731
32+ // For "generate openapi", consume the subcommand
33+ if command = " generate" && i < argv.Length && not ( argv[ i]. StartsWith " --" ) then
34+ subCommand <- Some argv[ i]
35+ i <- i + 1
36+
2837 while i < argv.Length do
2938 match argv[ i] with
3039 | " --env" when i + 1 < argv.Length ->
@@ -38,6 +47,9 @@ let parseArgs (argv: string array) : CliArgs =
3847 | " --output" when i + 1 < argv.Length ->
3948 output <- argv[ i + 1 ]
4049 i <- i + 2
50+ | " --output-dir" when i + 1 < argv.Length ->
51+ outputDir <- Some argv[ i + 1 ]
52+ i <- i + 2
4153 | " --verbose" ->
4254 verbose <- true
4355 i <- i + 1
@@ -47,20 +59,23 @@ let parseArgs (argv: string array) : CliArgs =
4759 | _ ->
4860 i <- i + 1
4961
50- { Command = command; File = file; Env = env; Vars = vars; Output = output; Verbose = verbose }
62+ { Command = command; SubCommand = subCommand; File = file; Env = env
63+ Vars = vars; Output = output; OutputDir = outputDir; Verbose = verbose }
5164
5265let printHelp () =
5366 printfn " Nap — API testing tool"
5467 printfn " "
5568 printfn " Usage:"
56- printfn " nap run <file|folder> Run a .nap file, .naplist playlist, or folder"
57- printfn " nap check <file> Validate a .nap or .naplist file"
58- printfn " nap help Show this help"
69+ printfn " nap run <file|folder> Run a .nap file, .naplist playlist, or folder"
70+ printfn " nap check <file> Validate a .nap or .naplist file"
71+ printfn " nap generate openapi <spec> --output-dir <dir> Generate .nap files from OpenAPI spec"
72+ printfn " nap help Show this help"
5973 printfn " "
6074 printfn " Options:"
6175 printfn " --env <name> Environment name (loads .napenv.<name>)"
6276 printfn " --var <key=value> Variable override (repeatable)"
6377 printfn " --output <format> Output: pretty (default), junit, json, ndjson"
78+ printfn " --output-dir <dir> Output directory for generate command"
6479 printfn " --verbose Enable debug-level logging"
6580
6681let runFile ( args : CliArgs ) : int =
@@ -208,6 +223,53 @@ let runFile (args: CliArgs) : int =
208223
209224 if result.Passed then 0 else 1
210225
226+ let private writeGenerated ( outDir : string ) ( result : OpenApiGenerator.GenerationResult ) : unit =
227+ let writeFile ( f : OpenApiGenerator.GeneratedFile ) =
228+ let fullPath = Path.Combine( outDir, f.FileName)
229+ let dir = Path.GetDirectoryName( fullPath)
230+ if not ( Directory.Exists dir) then
231+ Directory.CreateDirectory( dir) |> ignore
232+ File.WriteAllText( fullPath, f.Content)
233+ writeFile result.Environment
234+ for nap in result.NapFiles do
235+ writeFile nap
236+ writeFile result.Playlist
237+
238+ let generateOpenApi ( args : CliArgs ) : int =
239+ match args.File with
240+ | None ->
241+ eprintfn " Error: no spec file specified"
242+ eprintfn " Usage: nap generate openapi <spec.json> --output-dir <dir>"
243+ 2
244+ | Some specPath ->
245+ let specPath = Path.GetFullPath( specPath)
246+ if not ( File.Exists specPath) then
247+ eprintfn " Error: %s not found" specPath
248+ 2
249+ else
250+ let outDir =
251+ match args.OutputDir with
252+ | Some dir -> Path.GetFullPath( dir)
253+ | None -> Path.GetDirectoryName( specPath)
254+ let specContent = File.ReadAllText( specPath)
255+ match OpenApiGenerator.generate specContent with
256+ | Error msg ->
257+ eprintfn " Error: %s " msg
258+ 1
259+ | Ok generated ->
260+ if not ( Directory.Exists outDir) then
261+ Directory.CreateDirectory( outDir) |> ignore
262+ writeGenerated outDir generated
263+ match args.Output with
264+ | " json" ->
265+ printfn " {\" files\" :%d ,\" playlist\" :\" %s \" }" generated.NapFiles.Length generated.Playlist.FileName
266+ | _ ->
267+ printfn " Generated %d .nap files from OpenAPI spec" generated.NapFiles.Length
268+ printfn " Playlist: %s " generated.Playlist.FileName
269+ printfn " Environment: %s " generated.Environment.FileName
270+ printfn " Output: %s " outDir
271+ 0
272+
211273let checkFile ( args : CliArgs ) : int =
212274 match args.File with
213275 | None ->
@@ -243,6 +305,15 @@ let main argv =
243305 match args.Command with
244306 | " run" -> runFile args
245307 | " check" -> checkFile args
308+ | " generate" ->
309+ match args.SubCommand with
310+ | Some " openapi" -> generateOpenApi args
311+ | Some other ->
312+ eprintfn " Unknown generate target: %s " other
313+ 2
314+ | None ->
315+ eprintfn " Usage: nap generate openapi <spec.json> --output-dir <dir>"
316+ 2
246317 | " help" | " --help" | " -h" ->
247318 printHelp ()
248319 0
0 commit comments