@@ -156,6 +156,7 @@ fn is_nullable(ty: &Type<'_, String>) -> bool {
156156}
157157
158158/// Check if a type is a list.
159+ #[ allow( dead_code) ]
159160fn is_list ( ty : & Type < ' _ , String > ) -> bool {
160161 match ty {
161162 Type :: ListType ( _) => true ,
@@ -164,6 +165,16 @@ fn is_list(ty: &Type<'_, String>) -> bool {
164165 }
165166}
166167
168+ /// Count the list nesting depth of a type.
169+ /// `String` -> 0, `[String]` -> 1, `[[String]]` -> 2, etc.
170+ fn list_depth ( ty : & Type < ' _ , String > ) -> u8 {
171+ match ty {
172+ Type :: ListType ( inner) => 1 + list_depth ( inner) ,
173+ Type :: NonNullType ( inner) => list_depth ( inner) ,
174+ Type :: NamedType ( _) => 0 ,
175+ }
176+ }
177+
167178/// Check if a field has the @derivedFrom directive.
168179fn is_derived_field ( field : & Field < ' _ , String > ) -> bool {
169180 field. directives . iter ( ) . any ( |d| d. name == "derivedFrom" )
@@ -187,7 +198,8 @@ struct FieldInfo {
187198 is_derived : bool ,
188199 base_type : String ,
189200 is_nullable : bool ,
190- is_list : bool ,
201+ /// The nesting depth of list wrappers. 0 = scalar, 1 = [T], 2 = [[T]], etc.
202+ list_depth : u8 ,
191203}
192204
193205/// Schema code generator.
@@ -232,7 +244,7 @@ impl SchemaCodeGenerator {
232244 is_derived : is_derived_field ( f) ,
233245 base_type : get_base_type_name ( & f. field_type ) ,
234246 is_nullable : is_nullable ( & f. field_type ) ,
235- is_list : is_list ( & f. field_type ) ,
247+ list_depth : list_depth ( & f. field_type ) ,
236248 } )
237249 . collect ( ) ;
238250
@@ -557,24 +569,33 @@ impl SchemaCodeGenerator {
557569 }
558570
559571 /// Get the value type string for a field.
572+ ///
573+ /// Returns the GraphQL-style value type string:
574+ /// - Scalars: `String`, `Int`, `BigInt`, etc.
575+ /// - Arrays: `[String]`, `[Int]`, etc.
576+ /// - Nested arrays: `[[String]]`, `[[Int]]`, etc.
577+ /// - Entity references are converted to `String` (their ID type)
560578 fn value_type_from_field ( & self , field : & FieldInfo ) -> String {
561- if field. is_list {
562- format ! (
563- "[{}]" ,
564- if self . entity_names. contains( & field. base_type) {
565- "String" . to_string( )
566- } else {
567- field. base_type. clone( )
568- }
569- )
570- } else if self . entity_names . contains ( & field. base_type ) {
571- "String" . to_string ( )
579+ let base = if self . entity_names . contains ( & field. base_type ) {
580+ "String" . to_string ( ) // Entity references are stored as string IDs
572581 } else {
573582 field. base_type . clone ( )
583+ } ;
584+
585+ // Wrap with brackets for each level of list nesting
586+ let mut result = base;
587+ for _ in 0 ..field. list_depth {
588+ result = format ! ( "[{}]" , result) ;
574589 }
590+ result
575591 }
576592
577593 /// Convert field info to an AssemblyScript TypeExpr.
594+ ///
595+ /// Creates the correct type expression including nested arrays:
596+ /// - Scalars: `string`, `i32`, `BigInt`, etc.
597+ /// - Arrays: `Array<string>`, `Array<i32>`, etc.
598+ /// - Nested arrays: `Array<Array<string>>`, etc.
578599 fn type_from_field ( & self , field : & FieldInfo ) -> TypeExpr {
579600 let type_name = if self . entity_names . contains ( & field. base_type ) {
580601 "string" // Entity references are stored as string IDs
@@ -584,12 +605,13 @@ impl SchemaCodeGenerator {
584605
585606 let named = NamedType :: new ( type_name) ;
586607
587- if field. is_list {
588- let array = ArrayType :: new ( named) ;
608+ if field. list_depth > 0 {
609+ // Use ArrayType::with_depth to create nested array types
610+ let array_type = ArrayType :: with_depth ( named, field. list_depth ) ;
589611 if field. is_nullable {
590- NullableType :: new ( array ) . into ( )
612+ NullableType :: new ( array_type ) . into ( )
591613 } else {
592- array . into ( )
614+ array_type
593615 }
594616 } else if field. is_nullable && !named. is_primitive ( ) {
595617 NullableType :: new ( named) . into ( )
@@ -715,4 +737,141 @@ mod tests {
715737 "Array field should use Array<string> type"
716738 ) ;
717739 }
740+
741+ #[ test]
742+ fn test_nested_array_field ( ) {
743+ let schema = r#"
744+ type Matrix @entity {
745+ id: ID!
746+ stringMatrix: [[String!]!]!
747+ intMatrix: [[Int!]!]
748+ bigIntMatrix: [[BigInt!]!]!
749+ }
750+ "# ;
751+ let doc = parse_schema :: < String > ( schema) . unwrap ( ) ;
752+ let gen = SchemaCodeGenerator :: new ( & doc) ;
753+
754+ let classes = gen. generate_types ( true ) ;
755+ assert_eq ! ( classes. len( ) , 1 ) ;
756+
757+ let matrix = & classes[ 0 ] ;
758+ let output = matrix. to_string ( ) ;
759+
760+ // Verify nested array field getter/setter are generated
761+ assert ! (
762+ output. contains( "get stringMatrix()" ) ,
763+ "Should have stringMatrix getter"
764+ ) ;
765+ assert ! (
766+ output. contains( "set stringMatrix(" ) ,
767+ "Should have stringMatrix setter"
768+ ) ;
769+
770+ // Check the type is Array<Array<string>>
771+ assert ! (
772+ output. contains( "Array<Array<string>>" ) ,
773+ "Nested array field should use Array<Array<string>> type, got: {}" ,
774+ output
775+ ) ;
776+
777+ // Check that toStringMatrix() and fromStringMatrix() are used
778+ assert ! (
779+ output. contains( "toStringMatrix()" ) ,
780+ "Should use toStringMatrix() for nested string arrays"
781+ ) ;
782+ assert ! (
783+ output. contains( "fromStringMatrix(" ) ,
784+ "Should use Value.fromStringMatrix() for nested string arrays"
785+ ) ;
786+
787+ // Check BigInt matrix uses correct methods
788+ assert ! (
789+ output. contains( "Array<Array<BigInt>>" ) ,
790+ "BigInt matrix should use Array<Array<BigInt>> type"
791+ ) ;
792+ assert ! (
793+ output. contains( "toBigIntMatrix()" ) ,
794+ "Should use toBigIntMatrix() for nested BigInt arrays"
795+ ) ;
796+ assert ! (
797+ output. contains( "fromBigIntMatrix(" ) ,
798+ "Should use Value.fromBigIntMatrix() for nested BigInt arrays"
799+ ) ;
800+
801+ // Check nullable nested array has correct type
802+ assert ! (
803+ output. contains( "Array<Array<i32>> | null" ) ,
804+ "Nullable nested array should be Array<Array<i32>> | null"
805+ ) ;
806+ }
807+
808+ #[ test]
809+ fn test_list_depth ( ) {
810+ use graphql_parser:: parse_schema;
811+
812+ // Helper to get list depth from schema field type
813+ fn get_field_list_depth ( schema_str : & str ) -> u8 {
814+ let doc = parse_schema :: < String > ( schema_str) . unwrap ( ) ;
815+ for def in & doc. definitions {
816+ if let Definition :: TypeDefinition ( TypeDefinition :: Object ( obj) ) = def {
817+ for field in & obj. fields {
818+ if field. name == "field" {
819+ return list_depth ( & field. field_type ) ;
820+ }
821+ }
822+ }
823+ }
824+ panic ! ( "Field not found" ) ;
825+ }
826+
827+ // Scalar
828+ assert_eq ! (
829+ get_field_list_depth( "type T @entity { id: ID!, field: String! }" ) ,
830+ 0
831+ ) ;
832+
833+ // Simple array
834+ assert_eq ! (
835+ get_field_list_depth( "type T @entity { id: ID!, field: [String!]! }" ) ,
836+ 1
837+ ) ;
838+
839+ // Nested array (matrix)
840+ assert_eq ! (
841+ get_field_list_depth( "type T @entity { id: ID!, field: [[String!]!]! }" ) ,
842+ 2
843+ ) ;
844+
845+ // Triple nested array
846+ assert_eq ! (
847+ get_field_list_depth( "type T @entity { id: ID!, field: [[[String!]!]!]! }" ) ,
848+ 3
849+ ) ;
850+ }
851+
852+ #[ test]
853+ fn test_value_type_from_field_nested ( ) {
854+ let schema = r#"
855+ type Test @entity {
856+ id: ID!
857+ scalar: String!
858+ array: [String!]!
859+ matrix: [[String!]!]!
860+ }
861+ "# ;
862+ let doc = parse_schema :: < String > ( schema) . unwrap ( ) ;
863+ let gen = SchemaCodeGenerator :: new ( & doc) ;
864+
865+ // Find the entity
866+ let entity = & gen. entities [ 0 ] ;
867+
868+ // Find each field and check its value type
869+ let scalar_field = entity. fields . iter ( ) . find ( |f| f. name == "scalar" ) . unwrap ( ) ;
870+ let array_field = entity. fields . iter ( ) . find ( |f| f. name == "array" ) . unwrap ( ) ;
871+ let matrix_field = entity. fields . iter ( ) . find ( |f| f. name == "matrix" ) . unwrap ( ) ;
872+
873+ assert_eq ! ( gen . value_type_from_field( scalar_field) , "String" ) ;
874+ assert_eq ! ( gen . value_type_from_field( array_field) , "[String]" ) ;
875+ assert_eq ! ( gen . value_type_from_field( matrix_field) , "[[String]]" ) ;
876+ }
718877}
0 commit comments