@@ -32,6 +32,98 @@ type SchemeJSON struct {
3232 Extra map [string ]any `json:"extra,omitempty"`
3333}
3434
35+ // Registry of custom scheme builders.
36+ // Key: type name (case-sensitive), Value: builder function.
37+ var customSchemeBuilders = map [string ]func (SchemeJSON ) Scheme {}
38+
39+ // RegisterSchemeType registers a custom Scheme builder for a given type name.
40+ //
41+ // Usage:
42+ //
43+ // scheme.RegisterSchemeType("MyCustomType", func(js scheme.SchemeJSON) scheme.Scheme {
44+ // // Build your own Scheme based on js
45+ // return SString.WithWidth(js.Width) // or any custom logic
46+ // })
47+ //
48+ // Notes:
49+ // - Type names are case-sensitive ("MyCustomType" ≠ "mycustomtype").
50+ // - Panics if the type name is already registered (built-in or custom).
51+ // - Use UnregisterSchemeType to remove a custom type.
52+ //
53+ // This allows users to extend BuildScheme with their own types without
54+ // modifying the core switch.
55+ func RegisterSchemeType (typeName string , builder func (SchemeJSON ) Scheme ) {
56+ if typeName == "" {
57+ panic ("cannot register empty type name" )
58+ }
59+ if _ , exists := customSchemeBuilders [typeName ]; exists {
60+ panic ("scheme type already registered: " + typeName )
61+ }
62+ customSchemeBuilders [typeName ] = builder
63+ }
64+
65+ // UnregisterSchemeType removes a previously registered custom Scheme builder.
66+ //
67+ // Usage:
68+ //
69+ // scheme.UnregisterSchemeType("MyCustomType")
70+ //
71+ // If the type name is not found, the function does nothing.
72+ func UnregisterSchemeType (typeName string ) {
73+ delete (customSchemeBuilders , typeName )
74+ }
75+
76+ // BuildScheme constructs a Scheme instance from a SchemeJSON definition.
77+ //
78+ // It inspects the `Type` field of the provided SchemeJSON and returns the
79+ // corresponding Scheme. Built-in types include:
80+ //
81+ // - "bool" → SBool / SNullBool
82+ // - "int8" → SInt8 / SNullInt8
83+ // - "int16" → SInt16 with optional Range
84+ // - "int32" → SInt32 with optional Range
85+ // - "int64" → SInt64 with optional Range
86+ // - "date" → SDate with optional DateFrom/DateTo
87+ // - "float32" → SFloat32 / SNullFloat32
88+ // - "float64" → SFloat64 / SNullFloat64
89+ // - "string" → SString with optional width, exact, prefix, suffix, pattern
90+ // - "email" → SEmail
91+ // - "uri" → SURI
92+ // - "lang" → SLang
93+ // - "bytes" → SBytes / SVariableBytes
94+ // - "any" → SAny
95+ // - "tuple" → STuple / STupleNamed / STupleVal (with flatten/variableLength)
96+ // - "repeat" → SRepeat
97+ // - "map" → SMap
98+ // - "mapUnordered" → SMapUnordered / SMapUnorderedOptional
99+ // - "mapRepeat" → SMapRepeatRange
100+ // - "multicheck" → SMultiCheckNames
101+ // - "enum" → SEnum
102+ // - "color" → SColor
103+ //
104+ // If the type is not recognized, BuildScheme checks the custom registry
105+ // (see RegisterSchemeType) before panicking.
106+ //
107+ // Usage:
108+ //
109+ // js := SchemeJSON{Type: "string", Width: 20, Prefix: "ID_"}
110+ // s := BuildScheme(js)
111+ // s now validates strings up to 20 chars starting with "ID_"
112+ //
113+ // Custom type example:
114+ //
115+ // scheme.RegisterSchemeType("MyCustomType", func(js scheme.SchemeJSON) scheme.Scheme {
116+ // return SString.Pattern("[A-Z]{3}[0-9]{2}")
117+ // })
118+ // custom := BuildScheme(SchemeJSON{Type: "MyCustomType"})
119+ //
120+ // Notes:
121+ // - Type names are case-sensitive.
122+ // - Nullable fields are respected where applicable.
123+ // - RangeMin/RangeMax apply to numeric types.
124+ // - DateFrom/DateTo must be RFC3339 strings.
125+ // - For "mapUnordered", FieldNames and Schema must align in length.
126+ // - For "mapRepeat", Schema must contain exactly two entries.
35127func BuildScheme (js SchemeJSON ) Scheme {
36128 switch js .Type {
37129 case "bool" :
@@ -117,6 +209,8 @@ func BuildScheme(js SchemeJSON) Scheme {
117209 return SEmail (js .Nullable )
118210 case "uri" :
119211 return SURI (js .Nullable )
212+ case "lang" :
213+ return SLang (js .Nullable )
120214 case "bytes" :
121215 if js .Width > 0 {
122216 return SBytes (js .Width )
@@ -175,10 +269,18 @@ func BuildScheme(js SchemeJSON) Scheme {
175269 case "color" :
176270 return SColor (js .Nullable )
177271 default :
272+ // Check custom registry before panicking
273+ if builder , ok := customSchemeBuilders [js .Type ]; ok {
274+ return builder (js )
275+ }
178276 panic ("unknown scheme type: " + js .Type )
179277 }
180278}
181279
280+ // buildSchemas is an internal helper that converts a slice of SchemeJSON
281+ // definitions into a slice of Scheme instances by delegating to BuildScheme.
282+ // It preserves the order of the input list and is primarily used by composite
283+ // types (tuple, map, repeat, etc.) when constructing nested schemas.
182284func buildSchemas (list []SchemeJSON ) []Scheme {
183285 out := make ([]Scheme , len (list ))
184286 for i , sub := range list {
0 commit comments