@@ -91,6 +91,47 @@ impl NodeList {
9191 }
9292}
9393
94+ pub struct RBSHash {
95+ parser : * mut rbs_parser_t ,
96+ pointer : * mut rbs_hash ,
97+ }
98+
99+ impl RBSHash {
100+ pub fn new ( parser : * mut rbs_parser_t , pointer : * mut rbs_hash ) -> Self {
101+ Self { parser, pointer }
102+ }
103+
104+ /// Returns an iterator over the key-value pairs.
105+ #[ must_use]
106+ pub fn iter ( & self ) -> RBSHashIter {
107+ RBSHashIter {
108+ parser : self . parser ,
109+ current : unsafe { ( * self . pointer ) . head } ,
110+ }
111+ }
112+ }
113+
114+ pub struct RBSHashIter {
115+ parser : * mut rbs_parser_t ,
116+ current : * mut rbs_hash_node_t ,
117+ }
118+
119+ impl Iterator for RBSHashIter {
120+ type Item = ( Node , Node ) ;
121+
122+ fn next ( & mut self ) -> Option < Self :: Item > {
123+ if self . current . is_null ( ) {
124+ None
125+ } else {
126+ let pointer_data = unsafe { * self . current } ;
127+ let key = unsafe { Node :: new ( self . parser , pointer_data. key ) } ;
128+ let value = unsafe { Node :: new ( self . parser , pointer_data. value ) } ;
129+ self . current = pointer_data. next ;
130+ Some ( ( key, value) )
131+ }
132+ }
133+ }
134+
94135pub struct RBSLocation {
95136 pointer : * const rbs_location_t ,
96137 #[ allow( dead_code) ]
@@ -237,4 +278,52 @@ mod tests {
237278 panic ! ( "No literal type node found" ) ;
238279 }
239280 }
281+
282+ #[ test]
283+ fn test_rbs_hash_via_record_type ( ) {
284+ // RecordType stores its fields in an RBSHash via all_fields()
285+ let rbs_code = r#"type foo = { name: String, age: Integer }"# ;
286+ let signature = parse ( rbs_code. as_bytes ( ) ) ;
287+ assert ! ( signature. is_ok( ) , "Failed to parse RBS signature" ) ;
288+
289+ let signature_node = signature. unwrap ( ) ;
290+ if let Node :: TypeAlias ( type_alias) = signature_node. declarations ( ) . iter ( ) . next ( ) . unwrap ( )
291+ && let Node :: RecordType ( record) = type_alias. type_ ( )
292+ {
293+ let hash = record. all_fields ( ) ;
294+ let fields: Vec < _ > = hash. iter ( ) . collect ( ) ;
295+ assert_eq ! ( fields. len( ) , 2 , "Expected 2 fields in record" ) ;
296+
297+ // Build a map of field names to type names
298+ let mut field_types: Vec < ( String , String ) > = Vec :: new ( ) ;
299+ for ( key, value) in & fields {
300+ let Node :: Symbol ( sym) = key else {
301+ panic ! ( "Expected Symbol key" ) ;
302+ } ;
303+ let Node :: RecordFieldType ( field_type) = value else {
304+ panic ! ( "Expected RecordFieldType value" ) ;
305+ } ;
306+ let Node :: ClassInstanceType ( class_type) = field_type. type_ ( ) else {
307+ panic ! ( "Expected ClassInstanceType" ) ;
308+ } ;
309+
310+ let key_name = String :: from_utf8 ( sym. name ( ) . to_vec ( ) ) . unwrap ( ) ;
311+ let type_name_node = class_type. name ( ) ;
312+ let type_name_sym = type_name_node. name ( ) ;
313+ let type_name = String :: from_utf8 ( type_name_sym. name ( ) . to_vec ( ) ) . unwrap ( ) ;
314+ field_types. push ( ( key_name, type_name) ) ;
315+ }
316+
317+ assert ! (
318+ field_types. contains( & ( "name" . to_string( ) , "String" . to_string( ) ) ) ,
319+ "Expected 'name: String'"
320+ ) ;
321+ assert ! (
322+ field_types. contains( & ( "age" . to_string( ) , "Integer" . to_string( ) ) ) ,
323+ "Expected 'age: Integer'"
324+ ) ;
325+ } else {
326+ panic ! ( "Expected TypeAlias with RecordType" ) ;
327+ }
328+ }
240329}
0 commit comments