11import {
2+ getRegexpPattern ,
23 isCDDLArray , isGroup , isNamedGroupReference , isLiteralWithValue ,
34 isNativeTypeWithOperator , isUnNamedProperty , isPropertyReference ,
45 isRange , isVariable , pascalCase ,
5- type Assignment , type PropertyType , type PropertyReference ,
6+ type Assignment , type NativeTypeWithOperator , type PropertyType , type PropertyReference ,
67 type Property , type Array as CDDLArray , type Operator , type Group ,
78 type Variable , type Comment , type Tag
89} from 'cddl'
@@ -516,6 +517,72 @@ function getExtraItemsType (props: Property[], ctx: Context): string | undefined
516517 return `Union[${ uniqueTypes . join ( ', ' ) } ]`
517518}
518519
520+ function stringifyPythonLiteral ( value : string ) {
521+ return JSON . stringify ( value )
522+ }
523+
524+ function getTemplateAnnotatedPattern ( regexpPattern : string ) : string | undefined {
525+ const wildcard = '.+'
526+ if ( ! regexpPattern . includes ( wildcard ) || / [ \\ ( ) [ \] { } | ? * ^ $ ] / . test ( regexpPattern . replaceAll ( wildcard , '' ) ) ) {
527+ return
528+ }
529+
530+ const segments = regexpPattern . split ( wildcard )
531+ const parts : string [ ] = [ ]
532+
533+ for ( let i = 0 ; i < segments . length ; i ++ ) {
534+ const segment = segments [ i ]
535+ if ( segment . length > 0 ) {
536+ parts . push ( stringifyPythonLiteral ( segment ) )
537+ }
538+
539+ if ( i < segments . length - 1 ) {
540+ parts . push ( 'str' )
541+ }
542+ }
543+
544+ if ( parts . length === 0 || ! parts . includes ( 'str' ) ) {
545+ return
546+ }
547+
548+ return `Annotated[str, ${ parts . join ( ' + ' ) } ]`
549+ }
550+
551+ function resolveNativeTypeWithOperator ( t : NativeTypeWithOperator , ctx : Context ) : string | undefined {
552+ if ( typeof t . Type !== 'string' ) {
553+ return
554+ }
555+
556+ const mapped = NATIVE_TYPE_MAP [ t . Type ]
557+ if ( ! mapped ) {
558+ return
559+ }
560+
561+ const regexpPattern = getRegexpPattern ( t )
562+ if ( ! regexpPattern ) {
563+ if ( mapped === 'Any' ) {
564+ ctx . typingImports . add ( 'Any' )
565+ }
566+ return mapped
567+ }
568+
569+ const templateAnnotatedPattern = getTemplateAnnotatedPattern ( regexpPattern )
570+ if ( ! templateAnnotatedPattern ) {
571+ if ( mapped === 'Any' ) {
572+ ctx . typingImports . add ( 'Any' )
573+ }
574+ return mapped
575+ }
576+
577+ ctx . typingImports . add ( 'Annotated' )
578+ if ( ctx . pydantic ) {
579+ ctx . pydanticImports . add ( 'StringConstraints' )
580+ return `Annotated[${ mapped } , StringConstraints(pattern=${ JSON . stringify ( regexpPattern ) } )]`
581+ }
582+
583+ return templateAnnotatedPattern
584+ }
585+
519586// ---------------------------------------------------------------------------
520587// Type resolution
521588// ---------------------------------------------------------------------------
@@ -532,6 +599,14 @@ function resolveType (t: PropertyType, ctx: Context, options: ResolveTypeOptions
532599 throw new Error ( `Unknown native type: "${ t } "` )
533600 }
534601
602+ if ( isNativeTypeWithOperator ( t ) && typeof t . Type === 'string' ) {
603+ const resolved = resolveNativeTypeWithOperator ( t , ctx )
604+ if ( resolved ) {
605+ return resolved
606+ }
607+ throw new Error ( `Unknown native type with operator: ${ JSON . stringify ( t ) } ` )
608+ }
609+
535610 if ( ( t as any ) . Type && typeof ( t as any ) . Type === 'string' && NATIVE_TYPE_MAP [ ( t as any ) . Type ] ) {
536611 const mapped = NATIVE_TYPE_MAP [ ( t as any ) . Type ]
537612 if ( mapped === 'Any' ) {
0 commit comments