resolve.odin (1742B)
1 package jschema 2 3 import "core:os" 4 import "core:path/filepath" 5 import "core:strings" 6 7 // Resolves every pending $ref, loading relative schema files on demand. 8 // Loading a file can append more pending refs, so this drains by index. 9 @(private) 10 resolve_refs :: proc(b: ^Builder) -> Error { 11 for i := 0; i < len(b.pending); i += 1 { 12 pending := b.pending[i] 13 14 fragment := pending.ref 15 file_part := "" 16 if hash := strings.index_byte(pending.ref, '#'); hash >= 0 { 17 file_part = pending.ref[:hash] 18 fragment = pending.ref[hash:] 19 } else { 20 file_part = pending.ref 21 fragment = "#" 22 } 23 24 file_key := pending.file 25 if file_part != "" { 26 file_key = resolve_file(b, file_part, pending.dir) or_return 27 } 28 29 node, found := b.pointers[strings.concatenate({file_key, fragment})] 30 if !found { 31 return Resolve_Error{path = pending.file, ref = pending.ref} 32 } 33 b.pool.nodes[pending.node].ref = node 34 } 35 return nil 36 } 37 38 // Loads (once) the schema file at `name` relative to `dir` and registers its 39 // root as a named def. Returns the file's pointer-map key. 40 @(private) 41 resolve_file :: proc(b: ^Builder, name: string, dir: string) -> (key: string, err: Error) { 42 path := name 43 if !filepath.is_abs(name) { 44 path, _ = filepath.join({dir, name}) 45 } 46 if cleaned, aerr := filepath.abs(path); aerr == nil { 47 path = cleaned 48 } 49 50 if _, loaded := b.files[path]; loaded { 51 return path, nil 52 } 53 54 data, rerr := os.read_entire_file_from_path(path, context.allocator) 55 if rerr != nil { 56 return "", IO_Error{path = path, error = rerr} 57 } 58 59 root := parse_document(b, data, path, path, filepath.dir(path)) or_return 60 b.files[path] = root 61 stem := filepath.stem(filepath.base(path)) 62 append(&b.pool.defs, Def{name = stem, node = root}) 63 return path, nil 64 }