, nom URL- api/v1/rooms/UUID/tracks/UUID.
, : eof, trailing "/" (alt_complete! ).
ErrorKind::Verify: , , , .
alt_complete!: ( " " ) .
with!, .
The playground does not support nom, so this time there is no link.
extern crate nom;
extern crate uuid;
use uuid::Uuid;
named!(uuid<&str, Uuid>, preceded!(
tag_s!("/"),
map_res!(take_s!(36), str::parse)
));
enum ApiRequest {
Rooms,
Room { room: Uuid },
Tracks { room: Uuid },
Track { room: Uuid, track: Uuid },
}
macro_rules! with {
($i:expr, $var:ident: $submac:ident!( $($args:tt)* ) >> $($rest:tt)*) => {
do_parse!($i, $var: $submac!($($args)*) >> r: $($rest)* >> (r));
};
($i:expr, $var:ident: $submac:ident >> $($rest:tt)*) => {
do_parse!($i, $var: $submac >> r: $($rest)* >> (r));
};
}
named!(apiv1<&str, ApiRequest>, preceded!(tag_s!("/api/v1"),
alt_complete!(
preceded!(tag_s!("/rooms"), alt_complete!(
with!(room: uuid >> alt_complete!(
preceded!(tag_s!("/tracks"), alt_complete!(
with!(track: uuid >> alt_complete!(
value!(ApiRequest::Track{room, track})
))
|
value!(ApiRequest::Tracks{room})
))
|
value!(ApiRequest::Room{room})
))
|
value!(ApiRequest::Rooms)
))
)
));
named!(api<&str, ApiRequest>, terminated!(
alt_complete!(
apiv1
),
tuple!(
alt_complete!(tag_s!("/") | value!("")),
eof!()
)
));
fn main() {
use nom::IResult::*;
use nom::ErrorKind;
let room = Uuid::parse_str("e19c94cf-53eb-4048-9c94-7ae74ff6d912").unwrap();
let track = Uuid::parse_str("83d235e8-03cd-420d-a8c6-6e42440a5573").unwrap();
assert_eq!(api("/api/v1/rooms"), Done("", ApiRequest::Rooms));
assert_eq!(api("/api/v1/rooms/"), Done("", ApiRequest::Rooms));
assert_eq!(
api("/api/v1/rooms/e19c94cf-53eb-4048-9c94-7ae74ff6d912"),
Done("", ApiRequest::Room { room })
);
assert_eq!(
api("/api/v1/rooms/e19c94cf-53eb-4048-9c94-7ae74ff6d912/"),
Done("", ApiRequest::Room { room })
);
assert_eq!(
api("/api/v1/rooms/e19c94cf-53eb-4048-9c94-7ae74ff6d912/tracks"),
Done("", ApiRequest::Tracks { room })
);
assert_eq!(
api("/api/v1/rooms/e19c94cf-53eb-4048-9c94-7ae74ff6d912/tracks/"),
Done("", ApiRequest::Tracks { room })
);
assert_eq!(
api("/api/v1/rooms/e19c94cf-53eb-4048-9c94-7ae74ff6d912/tracks/83d235e8-03cd-420d-a8c6-6e42440a5573"),
Done("", ApiRequest::Track{room, track})
);
assert_eq!(
api("/api/v1/rooms/e19c94cf-53eb-4048-9c94-7ae74ff6d912/tracks/83d235e8-03cd-420d-a8c6-6e42440a5573/"),
Done("", ApiRequest::Track{room, track})
);
assert_eq!(api("/api/v1"), Error(ErrorKind::Alt));
assert_eq!(api("/api/v1/foo"), Error(ErrorKind::Alt));
assert_eq!(api("/api/v1/rooms/123"), Error(ErrorKind::Eof));
assert_eq!(
api("/api/v1/rooms/e19c94cf-53eb-4048-9c94-7ae74ff6d912/bar"),
Error(ErrorKind::Eof)
);
assert_eq!(
api("/api/v1/rooms/e19c94cf-53eb-4048-9c94-7ae74ff6d912/tracks/83d235e8-03cd-420d-a8c6-6e42440a5573/123"),
Error(ErrorKind::Eof)
);
assert_eq!(api("/api/v2"), Error(ErrorKind::Alt));
}
You can also use a more strict branching method alt_full_opt_slash!, which would ensure that the branch is consistent only if it has fully analyzed the input.
Then you can use the more “flat” way (although the nested branches should still work) to analyze the alternatives (although this means that you can parse some UUIDs several times, and now all errors look like this Alt):
macro_rules! alt_full_opt_slash {
(__impl_push2 ($i:expr,) ($($new:tt)*), $($rest:tt)*) => {
alt_full_opt_slash!(__impl ($i, $($new)*), $($rest)*)
};
(__impl_push2 ($i:expr, $($result:tt)+) ($($new:tt)*), $($rest:tt)*) => {
alt_full_opt_slash!(__impl ($i, $($result)+ | $($new)*), $($rest)*)
};
(__impl_push ($($result:tt)*) ($($new:tt)*), $($rest:tt)*) => {
alt_full_opt_slash!(__impl_push2 ($($result)*) (
terminated!(
$($new)*,
tuple!(
alt_complete!(tag_s!("/") | value!("")),
eof!()
)
)
), $($rest)*)
};
(__impl ($($result:tt)*), $e:ident | $($rest:tt)*) => {
alt_full_opt_slash!(__impl_push ($($result)*) ( $e ), $($rest)*)
};
(__impl ($($result:tt)*), $subrule:ident!( $($args:tt)*) | $($rest:tt)*) => {
alt_full_opt_slash!(__impl_push ($($result)*) ( $subrule!($($args)*) ), $($rest)*)
};
(__impl ($($result:tt)*), $subrule:ident!( $($args:tt)* ) => { $gen:expr } | $($rest:tt)*) => {
alt_full_opt_slash!(__impl_push ($($result)*) ( $subrule!($($args)*) => { $gen } ), $($rest)*)
};
(__impl ($($result:tt)*), $e:ident => { $gen:expr } | $($rest:tt)*) => {
alt_full_opt_slash!(__impl_push ($($result)*) ( $e => { $gen } ), $($rest)*)
};
(__impl ($i:expr, $($result:tt)*), __end) => {
alt_complete!($i, $($result)*)
};
($i:expr, $($rest:tt)*) => {{
alt_full_opt_slash!(__impl ($i, ), $($rest)* | __end)
}};
}
named!(apiv1<&str, ApiRequest>, preceded!(tag_s!("/api/v1"),
alt_full_opt_slash!(
do_parse!(
tag_s!("/rooms") >>
(ApiRequest::Rooms)
)
|
do_parse!(
tag_s!("/rooms") >>
room: uuid >>
(ApiRequest::Room{room})
)
|
do_parse!(
tag_s!("/rooms") >>
room: uuid >>
tag_s!("/tracks") >>
(ApiRequest::Tracks{room})
)
|
do_parse!(
tag_s!("/rooms") >>
room: uuid >>
tag_s!("/tracks") >>
track: uuid >>
(ApiRequest::Track{room, track})
)
)
));
named!(api<&str, ApiRequest>, alt_complete!(
apiv1
));