main.odin (4077B)
1 // jschema generates Odin type declarations from a JSON Schema document. 2 // 3 // Usage: 4 // jschema schema.json -o:types.odin generate from a file 5 // cat schema.json | jschema generate from stdin to stdout 6 // 7 // Flags: 8 // -o:<path> output file (default: stdout) 9 // -pkg:<name> package name of the generated file (default: schema) 10 // -root:<name> name of the root declaration (default: Root) 11 package main 12 13 import "core:fmt" 14 import "core:mem" 15 import "core:os" 16 import "core:path/filepath" 17 import "core:strings" 18 19 import "../../pkg/jschema" 20 21 main :: proc() { 22 when ODIN_DEBUG { 23 track: mem.Tracking_Allocator 24 mem.tracking_allocator_init(&track, context.allocator) 25 context.allocator = mem.tracking_allocator(&track) 26 defer { 27 for _, entry in track.allocation_map { 28 fmt.eprintfln("jschema: leaked %v bytes at %v", entry.size, entry.location) 29 } 30 for entry in track.bad_free_array { 31 fmt.eprintfln("jschema: bad free at %v", entry.location) 32 } 33 mem.tracking_allocator_destroy(&track) 34 } 35 } 36 37 os.exit(run()) 38 } 39 40 run :: proc() -> int { 41 input := "" 42 output := "" 43 opts := jschema.Options{} 44 45 for arg in os.args[1:] { 46 switch { 47 case arg == "-h" || arg == "--help": 48 fmt.println(USAGE) 49 return 0 50 case strings.has_prefix(arg, "-o:"): 51 output = arg[len("-o:"):] 52 case strings.has_prefix(arg, "-pkg:"): 53 opts.package_name = arg[len("-pkg:"):] 54 case strings.has_prefix(arg, "-openapi"): 55 rest := arg[len("-openapi"):] 56 switch { 57 case rest == "": 58 opts.openapi = true 59 case rest == ":true": 60 opts.openapi = true 61 case rest == ":false": 62 opts.openapi = false 63 case: 64 fmt.eprintfln("jschema: invalid value for -openapi %q\n%s", arg, USAGE) 65 return 2 66 } 67 case strings.has_prefix(arg, "-root:"): 68 opts.root_name = arg[len("-root:"):] 69 case strings.has_prefix(arg, "-"): 70 fmt.eprintfln("jschema: unknown flag %q\n%s", arg, USAGE) 71 return 2 72 case input != "": 73 fmt.eprintfln("jschema: multiple schema paths given\n%s", USAGE) 74 return 2 75 case: 76 input = arg 77 } 78 } 79 80 if input != "" && output != "" { 81 if err := jschema.generate(input, output, opts); err != nil { 82 report(err) 83 return 1 84 } 85 return 0 86 } 87 88 data: []u8 89 base_dir := "" 90 if input == "" { 91 stdin_data, rerr := os.read_entire_file_from_file(os.stdin, context.allocator) 92 if rerr != nil { 93 fmt.eprintfln("jschema: cannot read stdin: %v", rerr) 94 return 1 95 } 96 data = stdin_data 97 } else { 98 file_data, rerr := os.read_entire_file_from_path(input, context.allocator) 99 if rerr != nil { 100 fmt.eprintfln("jschema: cannot read %s: %v", input, rerr) 101 return 1 102 } 103 data = file_data 104 base_dir = filepath.dir(input) // substring of input; not freed 105 } 106 defer delete(data) 107 108 source, err := jschema.generate_source(data, opts, base_dir) 109 if err != nil { 110 report(err) 111 return 1 112 } 113 defer delete(source) 114 115 if output == "" { 116 os.write(os.stdout, transmute([]u8)source) 117 return 0 118 } 119 if werr := os.write_entire_file(output, source); werr != nil { 120 fmt.eprintfln("jschema: cannot write %s: %v", output, werr) 121 return 1 122 } 123 return 0 124 } 125 126 report :: proc(err: jschema.Error) { 127 switch specific in err { 128 case jschema.IO_Error: 129 fmt.eprintfln("jschema: cannot access %s: %v", specific.path, specific.error) 130 case jschema.Parse_Error: 131 fmt.eprintfln( 132 "jschema: %s:%d:%d: %s", 133 specific.path, 134 specific.pos.line, 135 specific.pos.column, 136 specific.message, 137 ) 138 case jschema.Resolve_Error: 139 fmt.eprintfln("jschema: %s: cannot resolve $ref %q", specific.path, specific.ref) 140 case mem.Allocator_Error: 141 fmt.eprintfln("jschema: out of memory: %v", specific) 142 } 143 } 144 145 USAGE :: `usage: jschema [schema.json] [-o:output.odin] [-pkg:name] [-root:name] [-openapi[:true|:false]] 146 147 Generates Odin type declarations from a JSON Schema (draft-07 or 2020-12). 148 Reads from stdin when no schema path is given; writes to stdout when -o is 149 not given. 150 151 OpenAPI idioms (if/then/else lowering, Or_Reference($T) generic union) are 152 auto-detected from a top-level "openapi" property. -openapi forces the 153 mode on; -openapi:false forces it off.`