22Base generator interface for all code generation targets.
33
44Defines the contract that all language generators must implement.
5+ Focuses on schema-level concerns while leaving type mapping to language generators.
56"""
67
78from abc import ABC , abstractmethod
1112
1213class GeneratorError (Exception ):
1314 """Base exception for code generation errors."""
15+
1416 pass
1517
1618
1719class CodeGenerator (ABC ):
1820 """Abstract base class for all code generators."""
19-
21+
2022 def __init__ (self , config : Optional [Dict [str , Any ]] = None ):
2123 """Initialize generator with optional configuration."""
2224 self .config = config or {}
23-
25+
2426 @property
2527 @abstractmethod
2628 def language_name (self ) -> str :
2729 """Return the name of the target language (e.g., 'go', 'python')."""
2830 pass
29-
31+
3032 @property
3133 @abstractmethod
3234 def file_extension (self ) -> str :
3335 """Return the file extension for generated files (e.g., '.go', '.py')."""
3436 pass
35-
37+
3638 @abstractmethod
3739 def generate (self , schemas : Dict [str , Schema ], root_schema_name : str ) -> str :
3840 """
3941 Generate code for all schemas.
40-
42+
4143 Args:
4244 schemas: Dictionary mapping schema names to Schema objects
4345 root_schema_name: Name of the main/root schema
44-
46+
4547 Returns:
4648 Generated code as a string
4749 """
4850 pass
49-
51+
5052 @abstractmethod
5153 def generate_single_schema (self , schema : Schema ) -> str :
5254 """
5355 Generate code for a single schema.
54-
56+
5557 Args:
5658 schema: Schema to generate code for
57-
59+
5860 Returns:
5961 Generated code for this schema only
6062 """
6163 pass
62-
63- @abstractmethod
64- def map_field_type (self , field : Field ) -> str :
65- """
66- Map a field to the target language's type system.
67-
68- Args:
69- field: Field to get type for
70-
71- Returns:
72- Type name in target language
73- """
74- pass
75-
64+
7665 def get_import_statements (self , schemas : Dict [str , Schema ]) -> List [str ]:
7766 """
7867 Get any required import statements for the generated code.
79-
68+
8069 Args:
8170 schemas: All schemas being generated
82-
71+
8372 Returns:
8473 List of import statements (can be empty)
8574 """
8675 return []
87-
76+
8877 def get_package_declaration (self ) -> Optional [str ]:
8978 """
9079 Get package/namespace declaration if needed.
91-
80+
9281 Returns:
9382 Package declaration string or None
9483 """
9584 return None
96-
85+
9786 def validate_schemas (self , schemas : Dict [str , Schema ]) -> List [str ]:
9887 """
99- Validate schemas for this generator and return any warnings.
100-
88+ Validate schemas for basic structural issues.
89+
90+ Language generators should override this to add language-specific validation.
91+
10192 Args:
10293 schemas: Schemas to validate
103-
94+
10495 Returns:
10596 List of warning messages (empty if no issues)
10697 """
10798 warnings = []
108-
99+
109100 for schema in schemas .values ():
101+ # Check for empty schemas
102+ if not schema .fields :
103+ warnings .append (f"Schema '{ schema .name } ' has no fields" )
104+
105+ # Check for schema-level conflicts and unknowns
110106 for field in schema .fields :
111107 if field .type == FieldType .CONFLICT :
108+ conflicting_type_names = (
109+ [t .value for t in field .conflicting_types ]
110+ if field .conflicting_types
111+ else ["unknown" ]
112+ )
112113 warnings .append (
113- f"Type conflict in { schema .name } .{ field .name } : "
114- f"{ [t .value for t in field .conflicting_types ]} "
114+ f"Type conflict in { schema .name } .{ field .name } : { conflicting_type_names } "
115115 )
116116 elif field .type == FieldType .UNKNOWN :
117+ warnings .append (f"Unknown type in { schema .name } .{ field .name } " )
118+
119+ # Check for missing nested schemas
120+ if field .type == FieldType .OBJECT and not field .nested_schema :
117121 warnings .append (
118- f"Unknown type in { schema .name } .{ field .name } "
122+ f"Object field { schema .name } .{ field .name } has no nested schema "
119123 )
120-
124+
125+ # Check for array fields with unclear element types
126+ if field .type == FieldType .ARRAY :
127+ if not field .array_element_type and not field .array_element_schema :
128+ warnings .append (
129+ f"Array field { schema .name } .{ field .name } has no element type information"
130+ )
131+
121132 return warnings
122-
133+
123134 def format_code (self , code : str ) -> str :
124135 """
125136 Apply language-specific formatting to generated code.
126-
137+
127138 Args:
128139 code: Raw generated code
129-
140+
130141 Returns:
131142 Formatted code
132143 """
133- return code # Default: no formatting
144+ # Basic cleanup - remove excessive blank lines
145+ lines = code .split ("\n " )
146+ formatted_lines = []
147+ blank_count = 0
148+
149+ for line in lines :
150+ stripped = line .rstrip ()
151+ if not stripped :
152+ blank_count += 1
153+ if blank_count <= 2 : # Allow max 2 consecutive blank lines
154+ formatted_lines .append ("" )
155+ else :
156+ blank_count = 0
157+ formatted_lines .append (stripped )
158+
159+ return "\n " .join (formatted_lines )
134160
135161
136162class GenerationResult :
137163 """Container for generation results and metadata."""
138-
139- def __init__ (self , code : str , warnings : List [str ] = None , metadata : Dict [str , Any ] = None ):
164+
165+ def __init__ (
166+ self , code : str , warnings : List [str ] = None , metadata : Dict [str , Any ] = None
167+ ):
140168 """
141169 Initialize generation result.
142-
170+
143171 Args:
144172 code: Generated code
145173 warnings: Any warnings from generation
@@ -149,52 +177,70 @@ def __init__(self, code: str, warnings: List[str] = None, metadata: Dict[str, An
149177 self .warnings = warnings or []
150178 self .metadata = metadata or {}
151179 self .success = True
152-
180+
153181 @classmethod
154- def error (cls , message : str , exception : Exception = None ) -> ' GenerationResult' :
182+ def error (cls , message : str , exception : Exception = None ) -> " GenerationResult" :
155183 """Create a failed generation result."""
156184 result = cls (code = "" )
157185 result .success = False
158186 result .error_message = message
159187 result .exception = exception
160188 return result
161189
190+ def add_warning (self , warning : str ):
191+ """Add a warning to the result."""
192+ self .warnings .append (warning )
193+
194+ def add_metadata (self , key : str , value : Any ):
195+ """Add metadata to the result."""
196+ self .metadata [key ] = value
197+
162198
163- def generate_code (generator : CodeGenerator , schemas : Dict [str , Schema ],
164- root_schema_name : str ) -> GenerationResult :
199+ def generate_code (
200+ generator : CodeGenerator , schemas : Dict [str , Schema ], root_schema_name : str
201+ ) -> GenerationResult :
165202 """
166203 Generate code using the specified generator with error handling.
167-
204+
168205 Args:
169206 generator: Code generator instance
170207 schemas: Schemas to generate code for
171208 root_schema_name: Name of the root schema
172-
209+
173210 Returns:
174211 GenerationResult with code, warnings, and metadata
175212 """
176213 try :
177- # Validate schemas
214+ # Validate schemas at the base level
178215 warnings = generator .validate_schemas (schemas )
179-
216+
180217 # Generate code
181218 code = generator .generate (schemas , root_schema_name )
182-
219+
183220 # Format code
184221 formatted_code = generator .format_code (code )
185-
222+
186223 # Create metadata
187224 metadata = {
188225 "language" : generator .language_name ,
189226 "file_extension" : generator .file_extension ,
190227 "schema_count" : len (schemas ),
191- "root_schema" : root_schema_name
228+ "root_schema" : root_schema_name ,
229+ "has_conflicts" : any (
230+ field .type == FieldType .CONFLICT
231+ for schema in schemas .values ()
232+ for field in schema .fields
233+ ),
234+ "has_unknowns" : any (
235+ field .type == FieldType .UNKNOWN
236+ for schema in schemas .values ()
237+ for field in schema .fields
238+ ),
192239 }
193-
194- return GenerationResult (formatted_code , warnings , metadata )
195-
240+
241+ result = GenerationResult (formatted_code , warnings , metadata )
242+
243+ return result
244+
196245 except Exception as e :
197- return GenerationResult .error (
198- f"Code generation failed: { str (e )} " ,
199- exception = e
200- )
246+ return GenerationResult .error (f"Code generation failed: { str (e )} " , exception = e )
0 commit comments