1
- use crate :: { ast:: AstToken , kinds:: SyntaxKind :: * } ;
1
+ use crate :: kinds:: SyntaxKind :: { TOKEN_PATH_ABS , TOKEN_PATH_HOME , TOKEN_PATH_REL } ;
2
+
2
3
use rowan:: { ast:: AstNode as OtherAstNode , NodeOrToken } ;
3
4
5
+ pub use super :: nodes:: Path ;
6
+ use super :: {
7
+ nodes:: { PathAbs , PathHome , PathRel , PathSearch } ,
8
+ AstToken , InterpolPart , PathContent ,
9
+ } ;
4
10
use crate :: ast;
5
11
6
- use super :: { InterpolPart , PathContent } ;
12
+ /// Base trait for all path node types
13
+ pub trait PathNode : ast:: AstNode { }
14
+
15
+ impl PathNode for PathAbs { }
16
+ impl PathNode for PathRel { }
17
+ impl PathNode for PathHome { }
18
+ impl PathNode for PathSearch { }
19
+ impl PathNode for Path { }
7
20
8
- impl ast:: nodes:: Path {
9
- pub fn parts ( & self ) -> impl Iterator < Item = InterpolPart < PathContent > > {
10
- self . syntax ( ) . children_with_tokens ( ) . map ( |child| match child {
21
+
22
+ fn extract_path_parts < T : ast:: AstNode > ( node : & T ) -> Vec < InterpolPart < PathContent > > {
23
+ node. syntax ( )
24
+ . children_with_tokens ( )
25
+ . map ( |child| match child {
11
26
NodeOrToken :: Token ( token) => {
12
- assert_eq ! ( token. kind( ) , TOKEN_PATH ) ;
27
+ debug_assert ! ( matches!(
28
+ token. kind( ) ,
29
+ TOKEN_PATH_ABS | TOKEN_PATH_REL | TOKEN_PATH_HOME
30
+ ) ) ;
13
31
InterpolPart :: Literal ( PathContent :: cast ( token) . unwrap ( ) )
14
32
}
15
33
NodeOrToken :: Node ( node) => {
16
34
InterpolPart :: Interpolation ( ast:: Interpol :: cast ( node. clone ( ) ) . unwrap ( ) )
17
35
}
18
36
} )
37
+ . collect ( )
38
+ }
39
+
40
+ // Direct methods for interpolatable path types
41
+ impl PathAbs {
42
+ pub fn parts ( & self ) -> Vec < InterpolPart < PathContent > > {
43
+ extract_path_parts ( self )
44
+ }
45
+ }
46
+
47
+ impl PathRel {
48
+ pub fn parts ( & self ) -> Vec < InterpolPart < PathContent > > {
49
+ extract_path_parts ( self )
50
+ }
51
+ }
52
+
53
+ impl PathHome {
54
+ pub fn parts ( & self ) -> Vec < InterpolPart < PathContent > > {
55
+ extract_path_parts ( self )
56
+ }
57
+ }
58
+
59
+ // Direct methods for search path
60
+ impl PathSearch {
61
+ /// Get the content of a search path
62
+ pub fn content ( & self ) -> Option < PathContent > {
63
+ self . syntax ( )
64
+ . children_with_tokens ( )
65
+ . filter_map ( |child| child. into_token ( ) . and_then ( PathContent :: cast) )
66
+ . next ( )
67
+ }
68
+ }
69
+
70
+ /// Extension methods for the Path enum
71
+ impl Path {
72
+ /// Get parts from any path type in a unified way
73
+ pub fn parts ( & self ) -> Vec < InterpolPart < PathContent > > {
74
+ match self {
75
+ // For interpolatable paths, get their parts
76
+ Path :: PathAbs ( p) => p. parts ( ) ,
77
+ Path :: PathRel ( p) => p. parts ( ) ,
78
+ Path :: PathHome ( p) => p. parts ( ) ,
79
+ // For search paths, return a single literal component if content exists
80
+ Path :: PathSearch ( p) => {
81
+ if let Some ( content) = p. content ( ) {
82
+ vec ! [ InterpolPart :: Literal ( content) ]
83
+ } else {
84
+ vec ! [ ]
85
+ }
86
+ }
87
+ }
88
+ }
89
+
90
+ pub fn is_search ( & self ) -> bool {
91
+ matches ! ( self , Path :: PathSearch ( _) )
92
+ }
93
+
94
+ pub fn is_interpolatable ( & self ) -> bool {
95
+ !self . is_search ( )
19
96
}
20
97
}
21
98
22
99
#[ cfg( test) ]
23
100
mod tests {
24
101
use rowan:: ast:: AstNode ;
25
102
103
+ use super :: InterpolPart ;
26
104
use crate :: {
27
- ast:: { self , AstToken , InterpolPart , PathContent } ,
105
+ ast:: { self , Path } ,
28
106
Root ,
29
107
} ;
30
108
31
109
#[ test]
32
- fn parts ( ) {
33
- fn assert_eq_ast_ctn ( it : & mut dyn Iterator < Item = InterpolPart < PathContent > > , x : & str ) {
34
- let tmp = it. next ( ) . expect ( "unexpected EOF" ) ;
35
- if let InterpolPart :: Interpolation ( astn) = tmp {
36
- assert_eq ! ( astn. expr( ) . unwrap( ) . syntax( ) . to_string( ) , x) ;
37
- } else {
38
- unreachable ! ( "unexpected literal {:?}" , tmp) ;
110
+ fn test_path_types ( ) {
111
+ // Absolute path
112
+ let inp = "/foo/bar" ;
113
+ let expr = Root :: parse ( inp) . ok ( ) . unwrap ( ) . expr ( ) . unwrap ( ) ;
114
+ if let ast:: Expr :: PathAbs ( p) = expr {
115
+ let path = Path :: cast ( p. syntax ( ) . clone ( ) ) . unwrap ( ) ;
116
+ assert ! ( path. is_interpolatable( ) ) ;
117
+ assert ! ( !path. is_search( ) ) ;
118
+ }
119
+
120
+ // Search path
121
+ let inp = "<nixpkgs>" ;
122
+ let expr = Root :: parse ( inp) . ok ( ) . unwrap ( ) . expr ( ) . unwrap ( ) ;
123
+ if let ast:: Expr :: PathSearch ( p) = expr {
124
+ let path = Path :: cast ( p. syntax ( ) . clone ( ) ) . unwrap ( ) ;
125
+ assert ! ( !path. is_interpolatable( ) ) ;
126
+ assert ! ( path. is_search( ) ) ;
127
+ }
128
+ }
129
+
130
+ #[ test]
131
+ fn test_parts ( ) {
132
+ // Test parts with absolute path
133
+ let inp = "/foo/bar" ;
134
+ let expr = Root :: parse ( inp) . ok ( ) . unwrap ( ) . expr ( ) . unwrap ( ) ;
135
+ if let ast:: Expr :: PathAbs ( p) = expr {
136
+ let path = Path :: cast ( p. syntax ( ) . clone ( ) ) . unwrap ( ) ;
137
+
138
+ let parts = path. parts ( ) ;
139
+ assert_eq ! ( parts. len( ) , 1 ) ;
140
+
141
+ match & parts[ 0 ] {
142
+ InterpolPart :: Literal ( content) => {
143
+ assert_eq ! ( content. text( ) , "/foo/bar" ) ;
144
+ }
145
+ _ => panic ! ( "Expected literal part" ) ,
146
+ }
147
+ }
148
+
149
+ // Test parts with interpolated path
150
+ let inp = r#"./a/${"hello"}"# ;
151
+ let expr = Root :: parse ( inp) . ok ( ) . unwrap ( ) . expr ( ) . unwrap ( ) ;
152
+ if let ast:: Expr :: PathRel ( p) = expr {
153
+ let path = Path :: cast ( p. syntax ( ) . clone ( ) ) . unwrap ( ) ;
154
+
155
+ let parts = path. parts ( ) ;
156
+ assert_eq ! ( parts. len( ) , 2 ) ;
157
+
158
+ match & parts[ 0 ] {
159
+ InterpolPart :: Literal ( content) => {
160
+ assert_eq ! ( content. text( ) , "./a/" ) ;
161
+ }
162
+ _ => panic ! ( "Expected literal part" ) ,
163
+ }
164
+
165
+ match & parts[ 1 ] {
166
+ InterpolPart :: Interpolation ( _) => { } // Success
167
+ _ => panic ! ( "Expected interpolation part" ) ,
39
168
}
40
169
}
41
170
42
- fn assert_eq_lit ( it : & mut dyn Iterator < Item = InterpolPart < PathContent > > , x : & str ) {
43
- let tmp = it. next ( ) . expect ( "unexpected EOF" ) ;
44
- if let InterpolPart :: Literal ( astn) = tmp {
45
- assert_eq ! ( astn. syntax( ) . text( ) , x) ;
46
- } else {
47
- unreachable ! ( "unexpected interpol {:?}" , tmp) ;
171
+ // Test parts with search path
172
+ let inp = "<nixpkgs>" ;
173
+ let expr = Root :: parse ( inp) . ok ( ) . unwrap ( ) . expr ( ) . unwrap ( ) ;
174
+ if let ast:: Expr :: PathSearch ( p) = expr {
175
+ let path = Path :: cast ( p. syntax ( ) . clone ( ) ) . unwrap ( ) ;
176
+
177
+ let parts = path. parts ( ) ;
178
+ assert_eq ! ( parts. len( ) , 1 ) ;
179
+
180
+ match & parts[ 0 ] {
181
+ InterpolPart :: Literal ( content) => {
182
+ assert_eq ! ( content. text( ) , "<nixpkgs>" ) ;
183
+ }
184
+ _ => panic ! ( "Expected literal part" ) ,
48
185
}
49
186
}
187
+ }
50
188
51
- let inp = r#"./a/b/${"c"}/${d}/e/f"# ;
189
+ #[ test]
190
+ fn direct_method_usage ( ) {
191
+ // Test direct parts() method on PathAbs
192
+ let inp = "/foo/bar" ;
52
193
let expr = Root :: parse ( inp) . ok ( ) . unwrap ( ) . expr ( ) . unwrap ( ) ;
53
- match expr {
54
- ast:: Expr :: Path ( p) => {
55
- let mut it = p. parts ( ) ;
56
- assert_eq_lit ( & mut it, "./a/b/" ) ;
57
- assert_eq_ast_ctn ( & mut it, "\" c\" " ) ;
58
- assert_eq_lit ( & mut it, "/" ) ;
59
- assert_eq_ast_ctn ( & mut it, "d" ) ;
60
- assert_eq_lit ( & mut it, "/e/f" ) ;
194
+ if let ast:: Expr :: PathAbs ( p) = expr {
195
+ let parts = p. parts ( ) ;
196
+ assert_eq ! ( parts. len( ) , 1 ) ;
197
+
198
+ match & parts[ 0 ] {
199
+ InterpolPart :: Literal ( content) => {
200
+ assert_eq ! ( content. text( ) , "/foo/bar" ) ;
201
+ }
202
+ _ => panic ! ( "Expected literal part" ) ,
61
203
}
62
- _ => unreachable ! ( ) ,
204
+ }
205
+
206
+ // Test direct content() method on PathSearch
207
+ let inp = "<nixpkgs>" ;
208
+ let expr = Root :: parse ( inp) . ok ( ) . unwrap ( ) . expr ( ) . unwrap ( ) ;
209
+ if let ast:: Expr :: PathSearch ( p) = expr {
210
+ let content = p. content ( ) . expect ( "Expected content" ) ;
211
+ assert_eq ! ( content. text( ) , "<nixpkgs>" ) ;
63
212
}
64
213
}
65
- }
214
+ }
0 commit comments