···67676868.PHONY: lexgen
6969lexgen: ## Run codegen tool for lexicons (lexicon JSON to Go packages)
7070- go run ./cmd/lexgen/ legacy --output-dir api
7070+ go run ./cmd/lexgen/ --build-file cmd/lexgen/bsky.json $(LEXDIR)
71717272.PHONY: cborgen
7373cborgen: ## Run codegen tool for CBOR serialization
+1-1
atproto/lexicon/testdata/catalog/procedure.json
···1616 "type": "integer",
1717 "description": "field of type integer"
1818 },
1919- "stringField": {
1919+ "string": {
2020 "type": "string",
2121 "description": "field of type string"
2222 }
···11-package lexgen
22-33-import (
44- "fmt"
55- "io"
66- "log/slog"
77- "sort"
88- "strings"
99-1010- "github.com/bluesky-social/indigo/atproto/lexicon"
1111- "github.com/bluesky-social/indigo/atproto/syntax"
1212-)
1313-1414-// Configuration for [CodeGenerator] output
1515-type GenConfig struct {
1616- RegisterLexiconTypeID bool
1717- PackageMappings map[string]string
1818- // one of: "type-decoder", "map-string-any", "json-raw-message"
1919- UnknownType string
2020- WarningText string
2121- LegacyMode bool
2222-}
2323-2424-func NewGenConfig() *GenConfig {
2525- return &GenConfig{
2626- UnknownType: "map-string-any",
2727- WarningText: "Code generated by indigo lexgen tool. DO NOT EDIT MANUALLY.",
2828- }
2929-}
3030-3131-func LegacyConfig() *GenConfig {
3232- return &GenConfig{
3333- RegisterLexiconTypeID: true,
3434- UnknownType: "type-decoder",
3535- WarningText: "Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT.",
3636- LegacyMode: true,
3737- }
3838-}
3939-4040-// Core implementation of Go code generation for a single Lexicon schema file (multiple definitions), building on pre-parsed [FlatLexicon]
4141-type CodeGenerator struct {
4242- Config *GenConfig
4343- Lex *FlatLexicon
4444- Cat lexicon.Catalog
4545- Out io.Writer
4646-}
4747-4848-// Outputs Go source code to the "Out" [io.Writer].
4949-func (gen *CodeGenerator) WriteLexicon() error {
5050-5151- if gen.Config.WarningText != "" {
5252- fmt.Fprintf(gen.Out, "// %s\n\n", gen.Config.WarningText)
5353- }
5454- fmt.Fprintf(gen.Out, "// Lexicon schema: %s\n\n", gen.Lex.NSID)
5555- fmt.Fprintf(gen.Out, "package %s\n\n", gen.PkgName())
5656- fmt.Fprintln(gen.Out, "import (")
5757- for dep, _ := range gen.deps() {
5858- fmt.Fprintf(gen.Out, " %s\n", dep)
5959- }
6060- fmt.Fprint(gen.Out, ")\n\n")
6161-6262- for _, ft := range gen.Lex.Types {
6363- slog.Info("generating type", "nsid", gen.Lex.NSID, "def", ft.DefName, "path", ft.Path, "type", ft.Type)
6464- if err := gen.WriteType(&ft); err != nil {
6565- return err
6666- }
6767- }
6868- return nil
6969-}
7070-7171-func (gen *CodeGenerator) PkgName() string {
7272- n := nsidPkgName(gen.Lex.NSID)
7373- if gen.Config.LegacyMode {
7474- switch n {
7575- case "appbsky":
7676- return "bsky"
7777- case "comatproto":
7878- return "atproto"
7979- case "toolsozone":
8080- return "ozone"
8181- case "chatbsky":
8282- return "chat"
8383- }
8484- }
8585- return n
8686-}
8787-8888-func (gen *CodeGenerator) baseName() string {
8989- // TODO: memoize this value? this method gets called a lot
9090- return nsidBaseName(gen.Lex.NSID)
9191-}
9292-9393-func (gen *CodeGenerator) FileName() string {
9494- return nsidFileName(gen.Lex.NSID) + ".go"
9595-}
9696-9797-func (gen *CodeGenerator) deps() map[string]bool {
9898- d := map[string]bool{
9999- "\"context\"": true,
100100- "\"fmt\"": true,
101101- "\"io\"": true,
102102- "\"bytes\"": true,
103103- "\"encoding/json\"": true,
104104- "lexutil \"github.com/bluesky-social/indigo/lex/util\"": true,
105105- "cbg \"github.com/whyrusleeping/cbor-gen\"": true,
106106- }
107107-108108- for ext, _ := range gen.Lex.ExternalRefs {
109109- // TODO: replace this with configurable/extensible mappings
110110- if strings.HasPrefix(ext, "com.atproto.") {
111111- d["comatproto \"github.com/bluesky-social/indigo/api/atproto\""] = true
112112- } else if strings.HasPrefix(ext, "app.bsky.") {
113113- d["appbsky \"github.com/bluesky-social/indigo/api/bsky\""] = true
114114- } else if strings.HasPrefix(ext, "tools.ozone.") {
115115- d["toolsozone \"github.com/bluesky-social/indigo/api/ozone\""] = true
116116- } else if strings.HasPrefix(ext, "chat.bsky.") {
117117- d["chatbsky \"github.com/bluesky-social/indigo/api/chat\""] = true
118118- } else {
119119- // TODO: configurable mappings; and return error if none found
120120- slog.Error("unhandled external reference", "ref", ext)
121121- }
122122- }
123123- return d
124124-}
125125-126126-func (gen *CodeGenerator) WriteType(ft *FlatType) error {
127127-128128- switch v := ft.Schema.Inner.(type) {
129129- case lexicon.SchemaRecord:
130130- if gen.Config.RegisterLexiconTypeID {
131131- fmt.Fprintf(gen.Out, "func init() {\n")
132132- fmt.Fprintf(gen.Out, " lexutil.RegisterType(\"%s\", &%s{})", gen.Lex.NSID, gen.baseName())
133133- fmt.Fprintf(gen.Out, "}\n\n")
134134- }
135135- // HACK: insert record-level description in to object if nil
136136- if v.Description != nil && v.Record.Description == nil {
137137- v.Record.Description = v.Description
138138- }
139139- if err := gen.writeStruct(ft, &v.Record); err != nil {
140140- return err
141141- }
142142- case lexicon.SchemaQuery:
143143- return gen.writeEndpoint(ft, defDescription(ft.Schema), v.Parameters, v.Output, nil, false)
144144- case lexicon.SchemaProcedure:
145145- return gen.writeEndpoint(ft, defDescription(ft.Schema), v.Parameters, v.Output, v.Input, true)
146146- case lexicon.SchemaSubscription:
147147- // pass; we only generate message types, not overall subscription
148148- case lexicon.SchemaPermissionSet, lexicon.SchemaPermission:
149149- // pass for Go codegen
150150- case lexicon.SchemaToken:
151151- // TODO: pass for now; could be a var/const?
152152- case lexicon.SchemaString, lexicon.SchemaInteger, lexicon.SchemaBoolean, lexicon.SchemaUnknown:
153153- // skip
154154- case lexicon.SchemaObject:
155155- if gen.Config.RegisterLexiconTypeID && ft.DefName == "main" && len(ft.Path) == 0 {
156156- fmt.Fprintf(gen.Out, "func init() {\n")
157157- fmt.Fprintf(gen.Out, " lexutil.RegisterType(\"%s#main\", &%s{})", gen.Lex.NSID, gen.baseName())
158158- fmt.Fprintf(gen.Out, "}\n\n")
159159- }
160160- if err := gen.writeStruct(ft, &v); err != nil {
161161- return err
162162- }
163163- case lexicon.SchemaUnion:
164164- return gen.writeUnion(ft, &v)
165165- case lexicon.SchemaRef:
166166- // skip for now. could be an alias type?
167167- default:
168168- return fmt.Errorf("unhandled schema type for codegen: %T", ft.Schema.Inner)
169169- }
170170-171171- return nil
172172-}
173173-174174-func isRequired(required []string, fname string) bool {
175175- for _, k := range required {
176176- if k == fname {
177177- return true
178178- }
179179- }
180180- return false
181181-}
182182-183183-func (gen *CodeGenerator) fieldType(fname string, def *lexicon.SchemaDef, optional bool) (string, error) {
184184- // NOTE: SchemaObject and SchemaUnion should be handled outside this function; as well as arrays of those types also count
185185- // TODO: another pass to check for type completeness
186186- switch v := def.Inner.(type) {
187187- case lexicon.SchemaNull:
188188- // NOTE: using "any" as a generic 'nil' type
189189- return "any", nil
190190- case lexicon.SchemaBoolean:
191191- if optional {
192192- return "*bool", nil
193193- } else {
194194- return "bool", nil
195195- }
196196- case lexicon.SchemaInteger:
197197- if optional {
198198- return "*int64", nil
199199- } else {
200200- return "int64", nil
201201- }
202202- case lexicon.SchemaString:
203203- if optional {
204204- return "*string", nil
205205- } else {
206206- return "string", nil
207207- }
208208- case lexicon.SchemaBytes:
209209- // NOTE: not using a pointer for optional
210210- return "lexutil.LexBytes", nil
211211- case lexicon.SchemaCIDLink:
212212- if optional {
213213- return "*lexutil.LexLink", nil
214214- } else {
215215- return "lexutil.LexLink", nil
216216- }
217217- case lexicon.SchemaBlob:
218218- if optional || gen.Config.LegacyMode {
219219- return "*lexutil.LexBlob", nil
220220- } else {
221221- return "lexutil.LexBlob", nil
222222- }
223223- case lexicon.SchemaArray:
224224- t, err := gen.fieldType(fname, &v.Items, false)
225225- if err != nil {
226226- return "", err
227227- }
228228- // NOTE: not using a pointer for optional
229229- return "[]" + t, nil
230230- case lexicon.SchemaUnknown:
231231- switch gen.Config.UnknownType {
232232- case "type-decoder":
233233- if gen.Config.LegacyMode && (fname == "didDoc" || fname == "plcOp" || fname == "meta" || fname == "debug") {
234234- if optional {
235235- return "*interface{}", nil
236236- } else {
237237- return "interface{}", nil
238238- }
239239- }
240240- return "*lexutil.LexiconTypeDecoder", nil
241241- case "json-raw-message":
242242- if optional {
243243- return "*json.RawMessage", nil
244244- } else {
245245- return "json.RawMessage", nil
246246- }
247247- case "map-string-any":
248248- return "map[string]any", nil
249249- default:
250250- return "map[string]any", nil
251251- }
252252- case lexicon.SchemaRef:
253253- ptr := ""
254254- if optional {
255255- ptr = "*"
256256- }
257257-258258- // check for local references to concrete types first
259259- if strings.HasPrefix(v.Ref, "#") {
260260- dt, ok := gen.Lex.Defs[v.Ref[1:]]
261261- if !ok {
262262- return "", fmt.Errorf("broken self-reference: %s", v.Ref)
263263- }
264264- switch dt.Type {
265265- case "string":
266266- if gen.Config.LegacyMode {
267267- ptr = "*"
268268- }
269269- return ptr + "string", nil
270270- case "integer":
271271- return ptr + "int64", nil
272272- case "boolean":
273273- return ptr + "bool", nil
274274- // TODO: "unknown", "ref", "token", etc
275275- case "array":
276276- // TODO: more completeness here (eg, non-object types)
277277- structPtr := ""
278278- if gen.Config.LegacyMode {
279279- structPtr = "*"
280280- }
281281- return fmt.Sprintf("[]%s%s_%s_Elem", structPtr, gen.baseName(), strings.Title(v.Ref[1:])), nil
282282- default: // presumed "object", "union"
283283- if gen.Config.LegacyMode {
284284- ptr = "*"
285285- }
286286- if v.Ref == "#main" {
287287- return ptr + gen.baseName(), nil
288288- }
289289- return fmt.Sprintf("%s%s_%s", ptr, gen.baseName(), strings.Title(v.Ref[1:])), nil
290290- }
291291- }
292292-293293- // external reference
294294- t, err := gen.externalRefType(v.Ref)
295295- if err != nil {
296296- return "", err
297297- }
298298- if gen.Config.LegacyMode {
299299- ptr = "*"
300300- }
301301- return ptr + t, nil
302302- default:
303303- return "", fmt.Errorf("unhandled schema type in struct field: %T", def.Inner)
304304- }
305305-}
306306-307307-func (gen *CodeGenerator) externalRefType(ref string) (string, error) {
308308- s, err := gen.Cat.Resolve(ref)
309309- if err != nil {
310310- return "", fmt.Errorf("could not resolve lexicon reference (%s): %w", ref, err)
311311- }
312312-313313- switch s.Def.(type) {
314314- case lexicon.SchemaString:
315315- return "string", nil
316316- // TODO: other concrete types and special-cases types, like arrays
317317- }
318318-319319- parts := strings.SplitN(ref, "#", 3)
320320- if len(parts) > 2 {
321321- return "", fmt.Errorf("failed to parse external ref: %s", ref)
322322- }
323323- nsid, err := syntax.ParseNSID(parts[0])
324324- if err != nil {
325325- return "", fmt.Errorf("failed to parse external ref NSID (%s): %w", ref, err)
326326- }
327327-328328- // check if this is actually in the same package (which might not mean the same NSID authority)
329329- if nsidPkgName(nsid) == nsidPkgName(gen.Lex.NSID) {
330330- if len(parts) == 1 || parts[1] == "main" {
331331- return nsidBaseName(nsid), nil
332332- } else {
333333- return fmt.Sprintf("%s_%s", nsidBaseName(nsid), strings.Title(parts[1])), nil
334334- }
335335- }
336336-337337- if len(parts) == 1 || parts[1] == "main" {
338338- return fmt.Sprintf("%s.%s", nsidPkgName(nsid), nsidBaseName(nsid)), nil
339339- } else {
340340- return fmt.Sprintf("%s.%s_%s", nsidPkgName(nsid), nsidBaseName(nsid), strings.Title(parts[1])), nil
341341- }
342342-}
343343-344344-func (gen *CodeGenerator) writeStruct(ft *FlatType, obj *lexicon.SchemaObject) error {
345345-346346- name := gen.baseName()
347347- if ft.DefName != "main" {
348348- name += "_" + strings.Title(ft.DefName)
349349- }
350350- for _, sub := range ft.Path {
351351- name += "_" + strings.Title(sub)
352352- }
353353-354354- if ft.DefName != "main" && len(ft.Path) == 0 {
355355- fmt.Fprintf(gen.Out, "// %s is a \"%s\" in the %s schema.\n", name, ft.DefName, gen.Lex.NSID)
356356- if obj.Description != nil {
357357- fmt.Fprintln(gen.Out, "//")
358358- }
359359- }
360360- if gen.Lex.Defs[ft.DefName].Type == "procedure" && len(ft.Path) == 1 && ft.Path[0] == "input" {
361361- // TODO: "request body"
362362- fmt.Fprintf(gen.Out, "// %s is the input argument to a %s call.\n", name, gen.Lex.NSID)
363363- }
364364- if (gen.Lex.Defs[ft.DefName].Type == "query" || gen.Lex.Defs[ft.DefName].Type == "procedure") && len(ft.Path) == 1 && ft.Path[0] == "output" {
365365- // TODO: "response body"
366366- fmt.Fprintf(gen.Out, "// %s is the output of a %s call.\n", name, gen.Lex.NSID)
367367- }
368368- skipDesc := false
369369- if gen.Config.LegacyMode && ft.Type == "record" {
370370- skipDesc = true
371371- }
372372- if obj.Description != nil && !skipDesc {
373373- for _, l := range strings.Split(*obj.Description, "\n") {
374374- fmt.Fprintf(gen.Out, "// %s\n", l)
375375- }
376376- }
377377- fmt.Fprintf(gen.Out, "type %s struct {\n", name)
378378-379379- // iterate field in sorted order
380380- fieldNames := []string{}
381381- for fname := range obj.Properties {
382382- fieldNames = append(fieldNames, fname)
383383- }
384384- sort.Strings(fieldNames)
385385-386386- // if this is a def-level struct, write out type decoder
387387- skipType := false
388388- if gen.Config.LegacyMode {
389389- // TODO: skip $type for all defs in subscription. this isn't robust!
390390- switch gen.Lex.MainType() {
391391- case "subscription":
392392- skipType = true
393393- }
394394- }
395395- if len(ft.Path) == 0 && !skipType {
396396- // TODO: can skip in some more situations?
397397- fullName := gen.Lex.NSID.String()
398398- if ft.DefName != "main" {
399399- fullName += "#" + ft.DefName
400400- }
401401- omitempty := ""
402402- if gen.Config.LegacyMode && gen.Lex.NSID.String() == "com.atproto.repo.strongRef" {
403403- omitempty = ",omitempty"
404404- }
405405- fmt.Fprintf(gen.Out, " LexiconTypeID string `json:\"$type%s\" cborgen:\"$type,const=%s%s\"`\n", omitempty, fullName, omitempty)
406406- }
407407-408408- for _, fname := range fieldNames {
409409- field := obj.Properties[fname]
410410- optional := false
411411- omitempty := ""
412412- if obj.IsNullable(fname) || !isRequired(obj.Required, fname) {
413413- optional = true
414414- omitempty = ",omitempty"
415415- }
416416-417417- var t string
418418- var err error
419419-420420- switch v := field.Inner.(type) {
421421- case lexicon.SchemaObject, lexicon.SchemaUnion:
422422- t = name + "_" + strings.Title(fname)
423423- if optional || gen.Config.LegacyMode {
424424- t = "*" + t
425425- }
426426- case lexicon.SchemaArray:
427427- switch v.Items.Inner.(type) {
428428- case lexicon.SchemaObject, lexicon.SchemaUnion:
429429- elemPtr := ""
430430- if gen.Config.LegacyMode {
431431- elemPtr = "*"
432432- }
433433- // NOTE: not using ptr for optional
434434- t = fmt.Sprintf("[]%s%s_%s_Elem", elemPtr, name, strings.Title(fname))
435435- default:
436436- t, err = gen.fieldType(fname, &field, optional)
437437- if err != nil {
438438- return err
439439- }
440440- }
441441- default:
442442- t, err = gen.fieldType(fname, &field, optional)
443443- if err != nil {
444444- return err
445445- }
446446- }
447447-448448- cborExtra := ""
449449- // HACK: copied from legacy code for now
450450- if gen.Lex.NSID.String() == "com.atproto.label.defs" && name == "LabelDefs_SelfLabels" && fname == "values" {
451451- cborExtra = ",preservenil"
452452- }
453453-454454- desc := defDescription(&field)
455455- if desc != "" {
456456- fmt.Fprintf(gen.Out, " // %s: %s\n", fname, desc)
457457- }
458458- fmt.Fprintf(gen.Out, " %s %s", strings.ReplaceAll(strings.Title(fname), "-", ""), t)
459459- fmt.Fprintf(gen.Out, " `json:\"%s%s\" cborgen:\"%s%s%s\"`\n", fname, omitempty, fname, omitempty, cborExtra)
460460- }
461461- fmt.Fprintf(gen.Out, "}\n\n")
462462-463463- return nil
464464-}
465465-466466-type unionRef struct {
467467- FieldName string
468468- TypeName string
469469- LexName string
470470-}
471471-472472-func (gen *CodeGenerator) writeUnion(ft *FlatType, union *lexicon.SchemaUnion) error {
473473-474474- name := gen.baseName()
475475- if ft.DefName != "main" {
476476- name += "_" + strings.Title(ft.DefName)
477477- }
478478- for _, sub := range ft.Path {
479479- name += "_" + strings.Title(sub)
480480- }
481481-482482- unionRefs := map[string]unionRef{}
483483- refNames := []string{}
484484- for _, ref := range union.Refs {
485485- r := unionRef{
486486- LexName: ref,
487487- }
488488-489489- if strings.HasPrefix(ref, "#") {
490490- r.LexName = gen.Lex.NSID.String() + ref
491491- n := gen.baseName()
492492- if ref != "#main" {
493493- n += "_" + strings.Title(ref[1:])
494494- }
495495- r.FieldName = n
496496- r.TypeName = n
497497- } else {
498498- n, err := gen.externalRefType(ref)
499499- if err != nil {
500500- return err
501501- }
502502- r.FieldName = n
503503- r.TypeName = n
504504- if strings.Contains(n, ".") {
505505- parts := strings.SplitN(n, ".", 2)
506506- r.FieldName = parts[1]
507507- }
508508- }
509509- refNames = append(refNames, r.FieldName)
510510- unionRefs[r.FieldName] = r
511511- }
512512- if !gen.Config.LegacyMode {
513513- sort.Strings(refNames)
514514- }
515515-516516- // first print out the union struct type
517517- if union.Description != nil {
518518- for _, l := range strings.Split(*union.Description, "\n") {
519519- fmt.Fprintf(gen.Out, "// %s\n", l)
520520- }
521521- }
522522- fmt.Fprintf(gen.Out, "type %s struct {\n", name)
523523- for _, rname := range refNames {
524524- ref := unionRefs[rname]
525525- fmt.Fprintf(gen.Out, " %s *%s\n", ref.FieldName, ref.TypeName)
526526- }
527527- fmt.Fprintf(gen.Out, "}\n\n")
528528-529529- // ... then MarshalJSON
530530- fmt.Fprintf(gen.Out, "func (t *%s) MarshalJSON() ([]byte, error) {\n", name)
531531- for _, rname := range refNames {
532532- ref := unionRefs[rname]
533533- fmt.Fprintf(gen.Out, " if t.%s != nil {\n", ref.FieldName)
534534- fmt.Fprintf(gen.Out, " t.%s.LexiconTypeID = \"%s\"\n", ref.FieldName, ref.LexName)
535535- fmt.Fprintf(gen.Out, " return json.Marshal(t.%s)\n", ref.FieldName)
536536- fmt.Fprintf(gen.Out, " }\n")
537537- }
538538- fmt.Fprintf(gen.Out, " return nil, fmt.Errorf(\"can not marshal empty union as JSON\")")
539539- fmt.Fprintf(gen.Out, "}\n\n")
540540-541541- // ... then UnmarshalJSON
542542- fmt.Fprintf(gen.Out, "func (t *%s) UnmarshalJSON(b []byte) error {\n", name)
543543- fmt.Fprintf(gen.Out, " typ, err := lexutil.TypeExtract(b)\n")
544544- fmt.Fprintf(gen.Out, " if err != nil {\n")
545545- fmt.Fprintf(gen.Out, " return err\n")
546546- fmt.Fprintf(gen.Out, " }\n\n")
547547- fmt.Fprintf(gen.Out, " switch typ {\n")
548548- for _, rname := range refNames {
549549- ref := unionRefs[rname]
550550- fmt.Fprintf(gen.Out, " case \"%s\":\n", ref.LexName)
551551- fmt.Fprintf(gen.Out, " t.%s = new(%s)\n", ref.FieldName, ref.TypeName)
552552- fmt.Fprintf(gen.Out, " return json.Unmarshal(b, t.%s)\n", ref.FieldName)
553553- }
554554- fmt.Fprintf(gen.Out, " default:\n")
555555- if union.Closed != nil && *union.Closed {
556556- // TODO: better error message
557557- fmt.Fprintf(gen.Out, " return fmt.Errorf(\"closed unions must match a listed schema\")\n")
558558- } else {
559559- fmt.Fprintf(gen.Out, " return nil\n")
560560- }
561561- fmt.Fprintf(gen.Out, " }\n")
562562- fmt.Fprintf(gen.Out, "}\n\n")
563563-564564- // only import CBOR marshalling of unions in legacy mode
565565- if !gen.Config.LegacyMode {
566566- return nil
567567- }
568568-569569- switch gen.Lex.MainType() {
570570- case "record", "subscription":
571571- // no-op
572572- case "object":
573573- // hacks for legacy serialization
574574- nsid := gen.Lex.NSID.String()
575575- if !(nsid == "app.bsky.richtext.facet" || (nsid == "app.bsky.embed.recordWithMedia" && ft.DefName == "main")) {
576576- return nil
577577- }
578578- default:
579579- return nil
580580- }
581581-582582- // ... then MarshalCBOR
583583- fmt.Fprintf(gen.Out, "func (t *%s) MarshalCBOR(w io.Writer) error {\n\n", name)
584584- fmt.Fprintf(gen.Out, " if t == nil {\n")
585585- fmt.Fprintf(gen.Out, " _, err := w.Write(cbg.CborNull)\n")
586586- fmt.Fprintf(gen.Out, " return err")
587587- fmt.Fprintf(gen.Out, " }\n")
588588- for _, rname := range refNames {
589589- ref := unionRefs[rname]
590590- fmt.Fprintf(gen.Out, " if t.%s != nil {\n", ref.FieldName)
591591- fmt.Fprintf(gen.Out, " return t.%s.MarshalCBOR(w)\n", ref.FieldName)
592592- fmt.Fprintf(gen.Out, " }\n")
593593- }
594594- fmt.Fprintf(gen.Out, " return fmt.Errorf(\"can not marshal empty union as CBOR\")")
595595- fmt.Fprintf(gen.Out, "}\n\n")
596596-597597- // ... then UnmarshalCBOR
598598- fmt.Fprintf(gen.Out, "func (t *%s) UnmarshalCBOR(r io.Reader) error {\n", name)
599599- fmt.Fprintf(gen.Out, " typ, b, err := lexutil.CborTypeExtractReader(r)\n")
600600- fmt.Fprintf(gen.Out, " if err != nil {\n")
601601- fmt.Fprintf(gen.Out, " return err\n")
602602- fmt.Fprintf(gen.Out, " }\n\n")
603603- fmt.Fprintf(gen.Out, " switch typ {\n")
604604- for _, rname := range refNames {
605605- ref := unionRefs[rname]
606606- fmt.Fprintf(gen.Out, " case \"%s\":\n", ref.LexName)
607607- fmt.Fprintf(gen.Out, " t.%s = new(%s)\n", ref.FieldName, ref.TypeName)
608608- fmt.Fprintf(gen.Out, " return t.%s.UnmarshalCBOR(bytes.NewReader(b))\n", ref.FieldName)
609609- }
610610- fmt.Fprintf(gen.Out, " default:\n")
611611- fmt.Fprintf(gen.Out, " return nil\n")
612612- fmt.Fprintf(gen.Out, " }\n")
613613- fmt.Fprintf(gen.Out, "}\n\n")
614614-615615- return nil
616616-}
617617-618618-func (gen *CodeGenerator) writeEndpoint(ft *FlatType, desc string, params *lexicon.SchemaParams, output, input *lexicon.SchemaBody, isProcedure bool) error {
619619- name := gen.baseName()
620620-621621- fmt.Fprintf(gen.Out, "// %s calls the XRPC method \"%s\".\n", name, gen.Lex.NSID)
622622- if desc != "" && !gen.Config.LegacyMode {
623623- fmt.Fprintln(gen.Out, "//")
624624- for _, l := range strings.Split(desc, "\n") {
625625- fmt.Fprintf(gen.Out, "// %s\n", l)
626626- }
627627- }
628628-629629- outputBytes := false
630630- outputStruct := ""
631631- if output != nil && output.Schema != nil {
632632- switch v := output.Schema.Inner.(type) {
633633- case lexicon.SchemaObject, lexicon.SchemaUnion:
634634- outputStruct = name + "_Output"
635635- case lexicon.SchemaRef:
636636- if strings.HasPrefix(v.Ref, "#") {
637637- // local reference
638638- outputStruct = fmt.Sprintf("%s_%s", gen.baseName(), strings.Title(v.Ref[1:]))
639639- } else {
640640- // external reference
641641- t, err := gen.externalRefType(v.Ref)
642642- if err != nil {
643643- return err
644644- }
645645- outputStruct = t
646646- }
647647- default:
648648- return fmt.Errorf("unsupported endpoint output schema def type: %T", output.Schema.Inner)
649649- }
650650- } else if output != nil && output.Encoding != "" {
651651- outputBytes = true
652652- }
653653-654654- paramNames := []string{}
655655- if params != nil {
656656- for name := range params.Properties {
657657- paramNames = append(paramNames, name)
658658- }
659659- }
660660- sort.Strings(paramNames)
661661-662662- args := []string{"ctx context.Context", "c lexutil.LexClient"}
663663- reqParams := []string{}
664664- optParams := []string{}
665665- if len(paramNames) > 0 {
666666- fmt.Fprintln(gen.Out, "//")
667667- for _, name := range paramNames {
668668- param := params.Properties[name]
669669- ptr := "*"
670670- if isRequired(params.Required, name) {
671671- ptr = ""
672672- reqParams = append(reqParams, name)
673673- } else {
674674- optParams = append(optParams, name)
675675- }
676676- switch v := param.Inner.(type) {
677677- case lexicon.SchemaBoolean:
678678- if v.Description != nil && *v.Description != "" {
679679- fmt.Fprintf(gen.Out, "// %s: %s\n", name, *v.Description)
680680- }
681681- if gen.Config.LegacyMode {
682682- ptr = ""
683683- }
684684- args = append(args, fmt.Sprintf("%s %sbool", name, ptr))
685685- case lexicon.SchemaInteger:
686686- if v.Description != nil && *v.Description != "" {
687687- fmt.Fprintf(gen.Out, "// %s: %s\n", name, *v.Description)
688688- }
689689- if gen.Config.LegacyMode {
690690- ptr = ""
691691- }
692692- args = append(args, fmt.Sprintf("%s %sint64", name, ptr))
693693- case lexicon.SchemaString:
694694- if v.Description != nil && *v.Description != "" {
695695- fmt.Fprintf(gen.Out, "// %s: %s\n", name, *v.Description)
696696- }
697697- args = append(args, fmt.Sprintf("%s string", name))
698698- case lexicon.SchemaUnknown:
699699- if v.Description != nil && *v.Description != "" {
700700- fmt.Fprintf(gen.Out, "// %s: %s\n", name, *v.Description)
701701- }
702702- args = append(args, fmt.Sprintf("%s any", name))
703703- case lexicon.SchemaArray:
704704- if v.Description != nil && *v.Description != "" {
705705- suffix := "[]"
706706- if gen.Config.LegacyMode {
707707- suffix = ""
708708- }
709709- fmt.Fprintf(gen.Out, "// %s%s: %s\n", name, suffix, *v.Description)
710710- }
711711- switch v.Items.Inner.(type) {
712712- case lexicon.SchemaBoolean:
713713- args = append(args, fmt.Sprintf("%s []bool", name))
714714- case lexicon.SchemaInteger:
715715- args = append(args, fmt.Sprintf("%s []int64", name))
716716- case lexicon.SchemaString:
717717- args = append(args, fmt.Sprintf("%s []string", name))
718718- default:
719719- return fmt.Errorf("unsupported parameter array type: %T", param.Inner)
720720- }
721721- default:
722722- return fmt.Errorf("unsupported parameter type: %T", param.Inner)
723723- }
724724- }
725725- }
726726-727727- inputArg := "nil"
728728- inputEncoding := ""
729729- inputStruct := ""
730730- if isProcedure && input != nil && input.Schema != nil {
731731- inputArg = "input"
732732- inputEncoding = input.Encoding
733733- switch v := input.Schema.Inner.(type) {
734734- case lexicon.SchemaObject, lexicon.SchemaUnion:
735735- inputStruct = name + "_Input"
736736- case lexicon.SchemaRef:
737737- if strings.HasPrefix(v.Ref, "#") {
738738- // local reference
739739- inputStruct = fmt.Sprintf("%s_%s", gen.baseName(), strings.Title(v.Ref[1:]))
740740- } else {
741741- // external reference
742742- t, err := gen.externalRefType(v.Ref)
743743- if err != nil {
744744- return err
745745- }
746746- inputStruct = t
747747- }
748748- }
749749- args = append(args, fmt.Sprintf("input *%s", inputStruct))
750750- } else if isProcedure && input != nil && input.Encoding != "" {
751751- inputArg = "input"
752752- inputEncoding = input.Encoding
753753- args = append(args, "input io.Reader")
754754- }
755755-756756- doOutParam := ""
757757- returnType := ""
758758- fmt.Fprintf(gen.Out, "func %s(%s) ", name, strings.Join(args, ", "))
759759- if outputStruct != "" {
760760- fmt.Fprintf(gen.Out, "(*%s, error) {\n", outputStruct)
761761- fmt.Fprintf(gen.Out, " var out %s\n", outputStruct)
762762- if !gen.Config.LegacyMode {
763763- fmt.Fprintln(gen.Out, "")
764764- }
765765- doOutParam = "&out"
766766- returnType = "&out"
767767- } else if outputBytes {
768768- fmt.Fprintf(gen.Out, "([]byte, error) {\n")
769769- fmt.Fprintf(gen.Out, " buf := new(bytes.Buffer)\n\n")
770770- doOutParam = "buf"
771771- returnType = "buf.Bytes()"
772772- } else {
773773- fmt.Fprintf(gen.Out, "error {\n")
774774- doOutParam = "nil"
775775- }
776776- paramsArg := "nil"
777777- if params != nil && len(params.Properties) > 0 {
778778- paramsArg = "params"
779779- if gen.Config.LegacyMode {
780780- fmt.Fprintln(gen.Out, "")
781781- }
782782- // TODO: switch to map[string]any
783783- fmt.Fprintf(gen.Out, " params := map[string]interface{}{}\n")
784784- }
785785- for _, name := range optParams {
786786- param := params.Properties[name]
787787- switch param.Inner.(type) {
788788- case lexicon.SchemaString:
789789- fmt.Fprintf(gen.Out, " if %s != \"\" {\n", name)
790790- fmt.Fprintf(gen.Out, " params[\"%s\"] = %s\n", name, name)
791791- fmt.Fprintf(gen.Out, " }\n")
792792- case lexicon.SchemaArray:
793793- fmt.Fprintf(gen.Out, " if len(%s) != 0 {\n", name)
794794- fmt.Fprintf(gen.Out, " params[\"%s\"] = %s\n", name, name)
795795- fmt.Fprintf(gen.Out, " }\n")
796796- case lexicon.SchemaUnknown:
797797- fmt.Fprintf(gen.Out, " if %s != nil {\n", name)
798798- fmt.Fprintf(gen.Out, " params[\"%s\"] = %s\n", name, name)
799799- fmt.Fprintf(gen.Out, " }\n")
800800- case lexicon.SchemaInteger:
801801- if gen.Config.LegacyMode {
802802- fmt.Fprintf(gen.Out, " if %s != 0 {\n", name)
803803- fmt.Fprintf(gen.Out, " params[\"%s\"] = %s\n", name, name)
804804- fmt.Fprintf(gen.Out, " }\n")
805805- } else {
806806- fmt.Fprintf(gen.Out, " if %s != nil {\n", name)
807807- fmt.Fprintf(gen.Out, " params[\"%s\"] = *%s\n", name, name)
808808- fmt.Fprintf(gen.Out, " }\n")
809809- }
810810- case lexicon.SchemaBoolean:
811811- if gen.Config.LegacyMode {
812812- fmt.Fprintf(gen.Out, " if %s {\n", name)
813813- fmt.Fprintf(gen.Out, " params[\"%s\"] = %s\n", name, name)
814814- fmt.Fprintf(gen.Out, " }\n")
815815- } else {
816816- fmt.Fprintf(gen.Out, " if %s != nil {\n", name)
817817- fmt.Fprintf(gen.Out, " params[\"%s\"] = *%s\n", name, name)
818818- fmt.Fprintf(gen.Out, " }\n")
819819- }
820820- default:
821821- fmt.Fprintf(gen.Out, " if %s != nil {\n", name)
822822- fmt.Fprintf(gen.Out, " params[\"%s\"] = *%s\n", name, name)
823823- fmt.Fprintf(gen.Out, " }\n")
824824- }
825825- }
826826- for _, name := range reqParams {
827827- fmt.Fprintf(gen.Out, " params[\"%s\"] = %s\n", name, name)
828828- }
829829- if !gen.Config.LegacyMode {
830830- fmt.Fprintln(gen.Out, "")
831831- }
832832-833833- method := "lexutil.Query"
834834- if isProcedure {
835835- method = "lexutil.Procedure"
836836- }
837837-838838- fmt.Fprintf(gen.Out, " if err := c.LexDo(ctx, %s, \"%s\", \"%s\", %s, %s, %s); err != nil {\n", method, inputEncoding, gen.Lex.NSID, paramsArg, inputArg, doOutParam)
839839- if returnType != "" {
840840- fmt.Fprintf(gen.Out, " return nil, err\n")
841841- } else {
842842- fmt.Fprintf(gen.Out, " return err\n")
843843- }
844844- fmt.Fprintf(gen.Out, " }\n")
845845- if gen.Config.LegacyMode {
846846- fmt.Fprintln(gen.Out, "")
847847- }
848848- if returnType != "" {
849849- fmt.Fprintf(gen.Out, " return %s, nil\n", returnType)
850850- } else {
851851- fmt.Fprintf(gen.Out, " return nil\n")
852852- }
853853- fmt.Fprintf(gen.Out, "}\n\n")
854854-855855- return nil
856856-}
-17
lex/lexgen/doc.go
···11-/*
22-Package implementing Go code generation for lexicon schemas.
33-44-Used by the 'lexgen' CLI tool to output Go structs and client API helpers based on Lexicon schemas. This package currently includes a "legacy" mode to stay as close as possible to the previous code generation output.
55-66-WARNING: this package is still a work in progress. Both the package API and the generated code are likely to change, possibly in backwards-incompatible ways.
77-88-# Package Structure
99-1010-The package works in two steps:
1111-1212-- "flattening" parses a full lexicon schema file and copies nested type definitions in to a top-level array
1313-- code generation outputs a single Go source code file corresponding to a flattened lexicon schema file
1414-1515-Wrapping code is expected to handle code formatting and fixing imports (which mostly means removing unused imports).
1616-*/
1717-package lexgen
-247
lex/lexgen/flatten.go
···11-package lexgen
22-33-import (
44- "fmt"
55- "slices"
66- "sort"
77- "strings"
88-99- "github.com/bluesky-social/indigo/atproto/lexicon"
1010- "github.com/bluesky-social/indigo/atproto/syntax"
1111-)
1212-1313-// Intermediate representation of a complete lexicon schema file, containing one or more definitions.
1414-type FlatLexicon struct {
1515- NSID syntax.NSID
1616- Description *string
1717- ExternalRefs map[string]bool // NSID with optional ref
1818- Defs map[string]FlatDef
1919- Types []FlatType
2020-}
2121-2222-// Minimal context about an individual top-level schema definition: just the short name and schema type.
2323-type FlatDef struct {
2424- Name string
2525- Type string
2626-}
2727-2828-// An individual "type definition", which is a small unit of schema definition that corresponds to a named unit of generated code. For example, a struct or API endpoint.
2929-type FlatType struct {
3030- // the short name of the schema def that this type is under
3131- DefName string
3232- Path []string
3333- Type string
3434- Schema *lexicon.SchemaDef
3535-}
3636-3737-func FlattenSchemaFile(sf *lexicon.SchemaFile) (*FlatLexicon, error) {
3838- nsid, err := syntax.ParseNSID(sf.ID)
3939- if err != nil {
4040- return nil, err
4141- }
4242-4343- fl := FlatLexicon{
4444- NSID: nsid,
4545- Description: sf.Description,
4646- ExternalRefs: map[string]bool{},
4747- Defs: map[string]FlatDef{},
4848- Types: []FlatType{},
4949- }
5050-5151- // iterate defs in sorted order; except "main" is always first if present
5252- defNames := []string{}
5353- hasMain := false
5454- for name := range sf.Defs {
5555- if name == "main" {
5656- hasMain = true
5757- continue
5858- }
5959- defNames = append(defNames, name)
6060- }
6161- sort.Strings(defNames)
6262- if hasMain {
6363- defNames = append([]string{"main"}, defNames...)
6464- }
6565-6666- for _, name := range defNames {
6767- def := sf.Defs[name]
6868- if err := fl.flattenDef(name, &def); err != nil {
6969- return nil, err
7070- }
7171- }
7272-7373- return &fl, nil
7474-}
7575-7676-func (fl *FlatLexicon) flattenDef(name string, def *lexicon.SchemaDef) error {
7777-7878- t, err := defType(def)
7979- if err != nil {
8080- return err
8181- }
8282-8383- fd := FlatDef{
8484- Name: name,
8585- Type: t,
8686- }
8787- fl.Defs[name] = fd
8888-8989- return fl.flattenType(&fd, []string{}, def)
9090-}
9191-9292-func (fl *FlatLexicon) flattenType(fd *FlatDef, tpath []string, def *lexicon.SchemaDef) error {
9393-9494- t, err := defType(def)
9595- if err != nil {
9696- return err
9797- }
9898-9999- ft := FlatType{
100100- DefName: fd.Name,
101101- Path: slices.Clone(tpath),
102102- Type: t,
103103- Schema: def,
104104- }
105105-106106- switch v := def.Inner.(type) {
107107- case lexicon.SchemaRecord:
108108- fl.Types = append(fl.Types, ft)
109109- if err := fl.flattenObject(fd, tpath, &v.Record); err != nil {
110110- return err
111111- }
112112- case lexicon.SchemaQuery:
113113- // v.Properties: only boolean, integer, string, or unknown are allowed, so recursion not really needed?
114114- if v.Output != nil && v.Output.Schema != nil {
115115- tp := slices.Clone(tpath)
116116- tp = append(tp, "output")
117117- if err := fl.flattenType(fd, tp, v.Output.Schema); err != nil {
118118- return err
119119- }
120120- }
121121- fl.Types = append(fl.Types, ft)
122122- case lexicon.SchemaProcedure:
123123- // v.Properties: same as above
124124- if v.Input != nil && v.Input.Schema != nil {
125125- tp := slices.Clone(tpath)
126126- tp = append(tp, "input")
127127- if err := fl.flattenType(fd, tp, v.Input.Schema); err != nil {
128128- return err
129129- }
130130- }
131131- if v.Output != nil && v.Output.Schema != nil {
132132- tp := slices.Clone(tpath)
133133- tp = append(tp, "output")
134134- if err := fl.flattenType(fd, tp, v.Output.Schema); err != nil {
135135- return err
136136- }
137137- }
138138- fl.Types = append(fl.Types, ft)
139139- case lexicon.SchemaSubscription:
140140- // v.Properties: same as above
141141- if v.Message != nil {
142142- switch vv := v.Message.Schema.Inner.(type) {
143143- case lexicon.SchemaUnion:
144144- for _, ref := range vv.Refs {
145145- if !strings.HasPrefix(ref, "#") {
146146- fl.ExternalRefs[strings.TrimSuffix(ref, "#main")] = true
147147- }
148148- }
149149- default:
150150- return fmt.Errorf("subscription with non-union message schema: %T", v.Message.Schema.Inner)
151151- }
152152- }
153153- fl.Types = append(fl.Types, ft)
154154- case lexicon.SchemaObject:
155155- fl.Types = append(fl.Types, ft)
156156- if err := fl.flattenObject(fd, tpath, &v); err != nil {
157157- return err
158158- }
159159- case lexicon.SchemaRef:
160160- if !strings.HasPrefix(v.Ref, "#") {
161161- fl.ExternalRefs[strings.TrimSuffix(v.Ref, "#main")] = true
162162- }
163163- fl.Types = append(fl.Types, ft)
164164- case lexicon.SchemaUnion:
165165- for _, ref := range v.Refs {
166166- if !strings.HasPrefix(ref, "#") {
167167- fl.ExternalRefs[strings.TrimSuffix(ref, "#main")] = true
168168- }
169169- }
170170- fl.Types = append(fl.Types, ft)
171171- case lexicon.SchemaArray:
172172- // flatten the inner item
173173- tp := slices.Clone(tpath)
174174- tp = append(tp, "elem")
175175- if err := fl.flattenType(fd, tpath, &v.Items); err != nil {
176176- return err
177177- }
178178- // don't emit the array itself
179179- return nil
180180- case lexicon.SchemaString, lexicon.SchemaNull, lexicon.SchemaInteger, lexicon.SchemaBoolean, lexicon.SchemaUnknown, lexicon.SchemaBytes:
181181- // don't emit
182182- // NOTE: might want to emit some string "knownValue" lists in the future?
183183- case lexicon.SchemaCIDLink, lexicon.SchemaBlob:
184184- // don't emit
185185- case lexicon.SchemaToken:
186186- // pass-through (emit)
187187- fl.Types = append(fl.Types, ft)
188188- case lexicon.SchemaPermissionSet, lexicon.SchemaPermission:
189189- // pass-through (emit)
190190- fl.Types = append(fl.Types, ft)
191191- default:
192192- return fmt.Errorf("unsupported def type for flattening (%s): %T", fd.Name, def.Inner)
193193- }
194194-195195- return nil
196196-}
197197-198198-func (fl *FlatLexicon) flattenObject(fd *FlatDef, tpath []string, obj *lexicon.SchemaObject) error {
199199-200200- keys := []string{}
201201- for n := range obj.Properties {
202202- keys = append(keys, n)
203203- }
204204- sort.Strings(keys)
205205-206206- for _, fname := range keys {
207207- field := obj.Properties[fname]
208208- tp := slices.Clone(tpath)
209209- tp = append(tp, fname)
210210- switch v := field.Inner.(type) {
211211- case lexicon.SchemaNull, lexicon.SchemaBoolean, lexicon.SchemaInteger, lexicon.SchemaString, lexicon.SchemaBytes:
212212- // no-op
213213- case lexicon.SchemaCIDLink, lexicon.SchemaBlob, lexicon.SchemaUnknown:
214214- // no-op, but maybe set a flag on def?
215215- case lexicon.SchemaArray:
216216- tp = append(tp, "elem")
217217- if err := fl.flattenType(fd, tp, &v.Items); err != nil {
218218- return err
219219- }
220220- case lexicon.SchemaObject:
221221- if err := fl.flattenType(fd, tp, &field); err != nil {
222222- return err
223223- }
224224- case lexicon.SchemaRef:
225225- if !strings.HasPrefix(v.Ref, "#") {
226226- // remove any #main suffix
227227- fl.ExternalRefs[strings.TrimSuffix(v.Ref, "#main")] = true
228228- }
229229- case lexicon.SchemaUnion:
230230- if err := fl.flattenType(fd, tp, &field); err != nil {
231231- return err
232232- }
233233- default:
234234- return fmt.Errorf("unsupported field type for object flattening: %T", field.Inner)
235235- }
236236- }
237237- return nil
238238-}
239239-240240-// Returns the type of any "#main" definition in this file (or else an empty string)
241241-func (fl *FlatLexicon) MainType() string {
242242- main, ok := fl.Defs["main"]
243243- if !ok {
244244- return ""
245245- }
246246- return main.Type
247247-}
-160
lex/lexgen/util.go
···11-package lexgen
22-33-import (
44- "fmt"
55- "slices"
66- "strings"
77-88- "github.com/bluesky-social/indigo/atproto/lexicon"
99- "github.com/bluesky-social/indigo/atproto/syntax"
1010-1111- "golang.org/x/net/publicsuffix"
1212-)
1313-1414-func defType(sd *lexicon.SchemaDef) (string, error) {
1515- switch sd.Inner.(type) {
1616- case lexicon.SchemaRecord:
1717- return "record", nil
1818- case lexicon.SchemaQuery:
1919- return "query", nil
2020- case lexicon.SchemaProcedure:
2121- return "procedure", nil
2222- case lexicon.SchemaSubscription:
2323- return "subscription", nil
2424- case lexicon.SchemaPermissionSet:
2525- return "permission-set", nil
2626- case lexicon.SchemaPermission:
2727- return "permission", nil
2828- case lexicon.SchemaNull:
2929- return "null", nil
3030- case lexicon.SchemaBoolean:
3131- return "boolean", nil
3232- case lexicon.SchemaInteger:
3333- return "integer", nil
3434- case lexicon.SchemaString:
3535- return "string", nil
3636- case lexicon.SchemaBytes:
3737- return "bytes", nil
3838- case lexicon.SchemaCIDLink:
3939- return "cid-link", nil
4040- case lexicon.SchemaArray:
4141- return "array", nil
4242- case lexicon.SchemaObject:
4343- return "object", nil
4444- case lexicon.SchemaBlob:
4545- return "blob", nil
4646- case lexicon.SchemaParams:
4747- return "params", nil
4848- case lexicon.SchemaToken:
4949- return "token", nil
5050- case lexicon.SchemaRef:
5151- return "ref", nil
5252- case lexicon.SchemaUnion:
5353- return "union", nil
5454- case lexicon.SchemaUnknown:
5555- return "unknown", nil
5656- default:
5757- return "", fmt.Errorf("unhandled schema type: %T", sd.Inner)
5858- }
5959-}
6060-6161-func defDescription(sd *lexicon.SchemaDef) string {
6262- var desc *string
6363-6464- switch v := sd.Inner.(type) {
6565- case lexicon.SchemaRecord:
6666- desc = v.Description
6767- case lexicon.SchemaQuery:
6868- desc = v.Description
6969- case lexicon.SchemaProcedure:
7070- desc = v.Description
7171- case lexicon.SchemaSubscription:
7272- desc = v.Description
7373- case lexicon.SchemaPermissionSet:
7474- // TODO: extract *some* description?
7575- case lexicon.SchemaPermission:
7676- desc = v.Description
7777- case lexicon.SchemaNull:
7878- desc = v.Description
7979- case lexicon.SchemaBoolean:
8080- desc = v.Description
8181- case lexicon.SchemaInteger:
8282- desc = v.Description
8383- case lexicon.SchemaString:
8484- desc = v.Description
8585- case lexicon.SchemaBytes:
8686- desc = v.Description
8787- case lexicon.SchemaCIDLink:
8888- desc = v.Description
8989- case lexicon.SchemaArray:
9090- desc = v.Description
9191- case lexicon.SchemaObject:
9292- desc = v.Description
9393- case lexicon.SchemaBlob:
9494- desc = v.Description
9595- case lexicon.SchemaParams:
9696- desc = v.Description
9797- case lexicon.SchemaToken:
9898- desc = v.Description
9999- case lexicon.SchemaRef:
100100- desc = v.Description
101101- case lexicon.SchemaUnion:
102102- desc = v.Description
103103- case lexicon.SchemaUnknown:
104104- desc = v.Description
105105- }
106106- if desc != nil && *desc != "" {
107107- return *desc
108108- }
109109- return ""
110110-}
111111-112112-func isCompoundDef(sd *lexicon.SchemaDef) bool {
113113- switch sd.Inner.(type) {
114114- case lexicon.SchemaRecord, lexicon.SchemaQuery, lexicon.SchemaProcedure, lexicon.SchemaSubscription, lexicon.SchemaArray, lexicon.SchemaObject, lexicon.SchemaUnion:
115115- return true
116116- default:
117117- return false
118118- }
119119-}
120120-121121-func nsidPkgName(nsid syntax.NSID) string {
122122- domain := strings.ToLower(nsid.Authority())
123123- reg, err := publicsuffix.EffectiveTLDPlusOne(domain)
124124- if err != nil {
125125- return "FAIL"
126126- }
127127- parts := strings.Split(reg, ".")
128128- slices.Reverse(parts)
129129-130130- return strings.Join(parts, "")
131131-}
132132-133133-func nsidBaseName(nsid syntax.NSID) string {
134134- domain := strings.ToLower(nsid.Authority())
135135- reg, err := publicsuffix.EffectiveTLDPlusOne(domain)
136136- if err != nil {
137137- return "FAIL"
138138- }
139139- rem := domain[0 : len(domain)-len(reg)]
140140- parts := strings.Split(rem, ".")
141141- slices.Reverse(parts)
142142- parts = append(parts, nsid.Name())
143143- for i := range parts {
144144- parts[i] = strings.Title(parts[i])
145145- }
146146- return strings.Join(parts, "")
147147-}
148148-149149-func nsidFileName(nsid syntax.NSID) string {
150150- domain := strings.ToLower(nsid.Authority())
151151- reg, err := publicsuffix.EffectiveTLDPlusOne(domain)
152152- if err != nil {
153153- return "FAIL"
154154- }
155155- rem := domain[0 : len(domain)-len(reg)]
156156- parts := strings.Split(rem, ".")
157157- slices.Reverse(parts)
158158- parts = append(parts, nsid.Name())
159159- return strings.Join(parts, "")
160160-}