this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

lexicon: split out SchemaFile code to separate file, and refactor post-parse

+75 -29
+7 -21
atproto/lexicon/catalog.go
··· 50 50 51 51 // Inserts a schema loaded from a JSON file in to the catalog. 52 52 func (c *BaseCatalog) AddSchemaFile(sf SchemaFile) error { 53 - if sf.Lexicon != 1 { 54 - return fmt.Errorf("unsupported lexicon language version: %d", sf.Lexicon) 53 + 54 + if err := sf.CheckSchema(); err != nil { 55 + return err 55 56 } 57 + 56 58 base := sf.ID 57 59 for frag, def := range sf.Defs { 58 - if len(frag) == 0 || strings.Contains(frag, "#") || strings.Contains(frag, ".") { 59 - // TODO: more validation here? 60 - return fmt.Errorf("schema name invalid: %s", frag) 61 - } 62 60 name := base + "#" + frag 63 61 if _, ok := c.schemas[name]; ok { 64 62 return fmt.Errorf("catalog already contained a schema with name: %s", name) 65 63 } 66 - // "A file can have at most one definition with one of the "primary" types. Primary types should always have the name main. It is possible for main to describe a non-primary type." 67 - switch s := def.Inner.(type) { 68 - case SchemaRecord, SchemaQuery, SchemaProcedure, SchemaSubscription, SchemaPermissionSet: 69 - if frag != "main" { 70 - return fmt.Errorf("record, query, procedure, and subscription types must be 'main', not: %s", frag) 71 - } 72 - case SchemaToken: 73 - // add fully-qualified name to token 74 - s.fullName = name 75 - def.Inner = s 76 - } 77 - def.SetBase(base) 78 - if err := def.CheckSchema(); err != nil { 79 - return err 80 - } 81 64 s := Schema{ 82 65 ID: name, 83 66 Def: def.Inner, ··· 91 74 func (c *BaseCatalog) addSchemaFromBytes(b []byte) error { 92 75 var sf SchemaFile 93 76 if err := json.Unmarshal(b, &sf); err != nil { 77 + return err 78 + } 79 + if err := sf.FinishParse(); err != nil { 94 80 return err 95 81 } 96 82 if err := c.AddSchemaFile(sf); err != nil {
-8
atproto/lexicon/language.go
··· 12 12 "github.com/rivo/uniseg" 13 13 ) 14 14 15 - // Serialization helper type for top-level Lexicon schema JSON objects (files) 16 - type SchemaFile struct { 17 - Lexicon int `json:"lexicon"` // must be 1 18 - ID string `json:"id"` 19 - Description *string `json:"description,omitempty"` 20 - Defs map[string]SchemaDef `json:"defs"` 21 - } 22 - 23 15 // enum type to represent any of the schema fields 24 16 type SchemaDef struct { 25 17 Inner any
+68
atproto/lexicon/schemafile.go
··· 1 + package lexicon 2 + 3 + import ( 4 + "fmt" 5 + "strings" 6 + ) 7 + 8 + // Serialization helper type for top-level Lexicon schema JSON objects (files). 9 + // 10 + // Note that the [FinishParse] method should always be called after unmarshalling a SchemaFile from JSON. 11 + type SchemaFile struct { 12 + Lexicon int `json:"lexicon"` // must be 1 13 + ID string `json:"id"` 14 + Description *string `json:"description,omitempty"` 15 + Defs map[string]SchemaDef `json:"defs"` 16 + } 17 + 18 + // Helper method which should always be called after parsing a schema file (eg, from JSON). 19 + // 20 + // Does some very basic validation (eg, lexicon language version), and fills in 21 + // internal references (for example full name of tokens). 22 + func (sf *SchemaFile) FinishParse() error { 23 + if sf.Lexicon != 1 { 24 + return fmt.Errorf("unsupported lexicon language version: %d", sf.Lexicon) 25 + } 26 + base := sf.ID 27 + for frag, def := range sf.Defs { 28 + if len(frag) == 0 || strings.Contains(frag, "#") || strings.Contains(frag, ".") { 29 + // TODO: more validation here? 30 + return fmt.Errorf("schema name invalid: %s", frag) 31 + } 32 + name := base + "#" + frag 33 + switch s := def.Inner.(type) { 34 + case SchemaToken: 35 + // add fully-qualified name to token 36 + s.FullName = name 37 + def.Inner = s 38 + } 39 + def.SetBase(base) 40 + sf.Defs[frag] = def 41 + } 42 + return nil 43 + } 44 + 45 + // Calls [SchemaDef.CheckSchema] recursively over all defs 46 + func (sf *SchemaFile) CheckSchema() error { 47 + if sf.Lexicon != 1 { 48 + return fmt.Errorf("unsupported lexicon language version: %d", sf.Lexicon) 49 + } 50 + 51 + for frag, def := range sf.Defs { 52 + if len(frag) == 0 || strings.Contains(frag, "#") || strings.Contains(frag, ".") { 53 + // TODO: more validation here? 54 + return fmt.Errorf("schema name invalid: %s", frag) 55 + } 56 + // "A file can have at most one definition with one of the "primary" types. Primary types should always have the name main. It is possible for main to describe a non-primary type." 57 + switch def.Inner.(type) { 58 + case SchemaRecord, SchemaQuery, SchemaProcedure, SchemaSubscription, SchemaPermissionSet: 59 + if frag != "main" { 60 + return fmt.Errorf("record, query, procedure, and subscription types must be 'main', not: %s", frag) 61 + } 62 + } 63 + if err := def.CheckSchema(); err != nil { 64 + return err 65 + } 66 + } 67 + return nil 68 + }