1+ use std:: fmt;
2+
13use anyhow:: { Context , Result } ;
2- use serde:: { Deserialize , Serialize } ;
3- use turbo_tasks:: primitives:: Regex ;
4+ use regex:: Regex ;
5+ use serde:: {
6+ Deserialize , Deserializer , Serialize , Serializer ,
7+ de:: { Error as _, Visitor } ,
8+ ser:: SerializeTupleStruct ,
9+ } ;
410use turbopack_node:: route_matcher:: { Param , Params , RouteMatcherRef } ;
511
612/// A regular expression that matches a path, with named capture groups for the
713/// dynamic parts of the path.
8- #[ derive( Debug , Serialize , Deserialize , Eq , PartialEq ) ]
14+ #[ derive( Debug ) ]
915pub struct PathRegex {
1016 regex : Regex ,
1117 named_params : Vec < NamedParam > ,
1218}
1319
20+ impl PartialEq for PathRegex {
21+ fn eq ( & self , other : & Self ) -> bool {
22+ self . regex . as_str ( ) == other. regex . as_str ( ) && self . named_params == other. named_params
23+ }
24+ }
25+
26+ impl Eq for PathRegex { }
27+
28+ impl Serialize for PathRegex {
29+ fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
30+ where
31+ S : Serializer ,
32+ {
33+ let mut ts = serializer. serialize_tuple_struct ( "PathRegex" , 2 ) ?;
34+ ts. serialize_field ( self . regex . as_str ( ) ) ?;
35+ ts. serialize_field ( & self . named_params ) ?;
36+ ts. end ( )
37+ }
38+ }
39+
40+ impl < ' de > Deserialize < ' de > for PathRegex {
41+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
42+ where
43+ D : Deserializer < ' de > ,
44+ {
45+ struct PathRegexVisitor ;
46+
47+ impl < ' de > Visitor < ' de > for PathRegexVisitor {
48+ type Value = PathRegex ;
49+
50+ fn expecting ( & self , formatter : & mut fmt:: Formatter ) -> fmt:: Result {
51+ formatter. write_str ( "a PathRegex tuple struct" )
52+ }
53+
54+ fn visit_seq < A > ( self , mut seq : A ) -> Result < Self :: Value , A :: Error >
55+ where
56+ A : serde:: de:: SeqAccess < ' de > ,
57+ {
58+ let regex_str: String = seq
59+ . next_element ( ) ?
60+ . ok_or_else ( || A :: Error :: invalid_length ( 0 , & self ) ) ?;
61+ let named_params: Vec < NamedParam > = seq
62+ . next_element ( ) ?
63+ . ok_or_else ( || A :: Error :: invalid_length ( 1 , & self ) ) ?;
64+
65+ let regex = Regex :: new ( & regex_str)
66+ . map_err ( |e| A :: Error :: custom ( format ! ( "invalid regex: {e}" ) ) ) ?;
67+
68+ Ok ( PathRegex {
69+ regex,
70+ named_params,
71+ } )
72+ }
73+ }
74+
75+ deserializer. deserialize_tuple_struct ( "PathRegex" , 2 , PathRegexVisitor )
76+ }
77+ }
78+
1479#[ derive( Debug , Serialize , Deserialize , Eq , PartialEq ) ]
1580struct NamedParam {
1681 name : String ,
@@ -153,7 +218,7 @@ impl PathRegexBuilder {
153218 pub fn build ( mut self ) -> Result < PathRegex > {
154219 self . regex_str += "$" ;
155220 Ok ( PathRegex {
156- regex : Regex ( regex:: Regex :: new ( & self . regex_str ) . with_context ( || "invalid path regex" ) ?) ,
221+ regex : regex:: Regex :: new ( & self . regex_str ) . with_context ( || "invalid path regex" ) ?,
157222 named_params : self . named_params ,
158223 } )
159224 }
@@ -164,3 +229,18 @@ impl Default for PathRegexBuilder {
164229 Self :: new ( )
165230 }
166231}
232+
233+ #[ cfg( test) ]
234+ mod tests {
235+ use super :: * ;
236+
237+ #[ test]
238+ fn test_path_regex_serde_roundtrip ( ) {
239+ let path_regex = super :: super :: build_path_regex ( "/api/[version]/[[...path]]" ) . unwrap ( ) ;
240+
241+ let serialized = serde_json:: to_string ( & path_regex) . unwrap ( ) ;
242+ let deserialized: PathRegex = serde_json:: from_str ( & serialized) . unwrap ( ) ;
243+
244+ assert_eq ! ( path_regex, deserialized) ;
245+ }
246+ }
0 commit comments