fork of indigo with slightly nicer lexgen
at main 3.5 kB view raw
1package lex 2 3import ( 4 "encoding/json" 5 "fmt" 6 "os" 7 "strings" 8) 9 10// Schema is a lexicon json file 11// e.g. atproto/lexicons/app/bsky/feed/post.json 12// https://atproto.com/specs/lexicon 13type Schema struct { 14 // path of json file read 15 path string 16 17 // prefix of lexicon group, e.g. "app.bsky" or "com.atproto" 18 prefix string 19 20 // Lexicon version, e.g. 1 21 Lexicon int `json:"lexicon"` 22 ID string `json:"id"` 23 Defs map[string]*TypeSchema `json:"defs"` 24} 25 26func ReadSchema(f string) (*Schema, error) { 27 fi, err := os.Open(f) 28 if err != nil { 29 return nil, err 30 } 31 defer fi.Close() 32 33 var s Schema 34 if err := json.NewDecoder(fi).Decode(&s); err != nil { 35 return nil, err 36 } 37 s.path = f 38 39 return &s, nil 40} 41 42func (s *Schema) Name() string { 43 p := strings.Split(s.ID, ".") 44 return p[len(p)-2] + p[len(p)-1] 45} 46 47func (s *Schema) AllTypes(prefix string, defMap map[string]*ExtDef) []outputType { 48 var out []outputType 49 50 var walk func(name string, ts *TypeSchema, needsCbor bool) 51 walk = func(name string, ts *TypeSchema, needsCbor bool) { 52 if ts == nil { 53 panic(fmt.Sprintf("nil type schema in %q (%s)", name, s.ID)) 54 } 55 56 if needsCbor { 57 fmt.Println("Setting to record: ", name) 58 if name == "EmbedImages_View" { 59 panic("not ok") 60 } 61 ts.needsCbor = true 62 } 63 64 if name == "LabelDefs_SelfLabels" { 65 ts.needsType = true 66 } 67 68 ts.prefix = prefix 69 ts.id = s.ID 70 ts.defMap = defMap 71 if ts.Type == "object" || 72 (ts.Type == "union" && len(ts.Refs) > 0) { 73 out = append(out, outputType{ 74 Name: name, 75 Type: ts, 76 NeedsCbor: ts.needsCbor, 77 }) 78 79 for _, r := range ts.Refs { 80 refname := r 81 if strings.HasPrefix(refname, "#") { 82 refname = s.ID + r 83 } 84 85 ed, ok := defMap[refname] 86 if !ok { 87 panic(fmt.Sprintf("cannot find: %q", refname)) 88 } 89 90 fmt.Println("UNION REF", refname, name, needsCbor) 91 92 if needsCbor { 93 ed.Type.needsCbor = true 94 } 95 96 ed.Type.needsType = true 97 } 98 } 99 100 if ts.Type == "ref" { 101 refname := ts.Ref 102 if strings.HasPrefix(refname, "#") { 103 refname = s.ID + ts.Ref 104 } 105 106 sub, ok := defMap[refname] 107 if !ok { 108 panic(fmt.Sprintf("missing ref: %q", refname)) 109 } 110 111 if needsCbor { 112 sub.Type.needsCbor = true 113 } 114 } 115 116 for childname, val := range ts.Properties { 117 walk(name+"_"+strings.Title(childname), val, ts.needsCbor) 118 } 119 120 if ts.Items != nil { 121 walk(name+"_Elem", ts.Items, ts.needsCbor) 122 } 123 124 if ts.Input != nil { 125 if ts.Input.Schema == nil { 126 if ts.Input.Encoding != EncodingCBOR && 127 ts.Input.Encoding != EncodingANY && 128 ts.Input.Encoding != EncodingCAR && 129 ts.Input.Encoding != EncodingMP4 { 130 panic(fmt.Sprintf("strange input type def in %s", s.ID)) 131 } 132 } else { 133 walk(name+"_Input", ts.Input.Schema, ts.needsCbor) 134 } 135 } 136 137 if ts.Output != nil { 138 if ts.Output.Schema == nil { 139 if ts.Output.Encoding != EncodingCBOR && 140 ts.Output.Encoding != EncodingCAR && 141 ts.Output.Encoding != EncodingANY && 142 ts.Output.Encoding != EncodingJSONL && 143 ts.Output.Encoding != EncodingMP4 { 144 panic(fmt.Sprintf("strange output type def in %s", s.ID)) 145 } 146 } else { 147 walk(name+"_Output", ts.Output.Schema, ts.needsCbor) 148 } 149 } 150 151 if ts.Type == "record" { 152 ts.Record.needsType = true 153 walk(name, ts.Record, true) 154 } 155 156 } 157 158 tname := nameFromID(s.ID, prefix) 159 160 for name, def := range s.Defs { 161 n := tname + "_" + strings.Title(name) 162 if name == "main" { 163 n = tname 164 } 165 walk(n, def, def.needsCbor) 166 } 167 168 return out 169}