1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use std::hash::{Hash, Hasher};
use devise::syn;
use proc_macro::{Span, Diagnostic};
use http::uri::{UriPart, Path};
use http::route::RouteSegment;
use proc_macro_ext::{Diagnostics, StringLit, PResult, DResult};
crate use http::route::{Error, Kind, Source};
#[derive(Debug, Clone)]
crate struct Segment {
crate span: Span,
crate kind: Kind,
crate source: Source,
crate name: String,
crate index: Option<usize>,
}
impl Segment {
fn from<P: UriPart>(segment: RouteSegment<P>, span: Span) -> Segment {
let source = match P::DELIMITER {
'/' => Source::Path,
'&' => Source::Query,
_ => unreachable!("only paths and queries")
};
let (kind, index) = (segment.kind, segment.index);
Segment { span, kind, source, index, name: segment.name.into_owned() }
}
}
impl<'a> From<&'a syn::Ident> for Segment {
fn from(ident: &syn::Ident) -> Segment {
Segment {
kind: Kind::Static,
source: Source::Unknown,
span: ident.span().unstable(),
name: ident.to_string(),
index: None,
}
}
}
impl PartialEq for Segment {
fn eq(&self, other: &Segment) -> bool {
self.name == other.name
}
}
impl Eq for Segment { }
impl Hash for Segment {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
fn subspan(needle: &str, haystack: &str, span: Span) -> Span {
let index = needle.as_ptr() as usize - haystack.as_ptr() as usize;
StringLit::new(haystack, span).subspan(index..index + needle.len())
}
fn trailspan(needle: &str, haystack: &str, span: Span) -> Span {
let index = needle.as_ptr() as usize - haystack.as_ptr() as usize;
let lit = StringLit::new(haystack, span);
if needle.as_ptr() as usize > haystack.as_ptr() as usize {
lit.subspan((index - 1)..)
} else {
lit.subspan(index..)
}
}
fn into_diagnostic(
segment: &str,
source: &str,
span: Span,
error: &Error,
) -> Diagnostic {
let seg_span = subspan(segment, source, span);
match error {
Error::Empty => {
seg_span.error("parameter names cannot be empty")
}
Error::Ident(name) => {
seg_span.error(format!("`{}` is not a valid identifier", name))
.help("parameter names must be valid identifiers")
}
Error::Ignored => {
seg_span.error("parameters must be named")
.help("use a name such as `_guard` or `_param`")
}
Error::MissingClose => {
seg_span.error("parameter is missing a closing bracket")
.help(format!("did you mean '{}>'?", segment))
}
Error::Malformed => {
seg_span.error("malformed parameter or identifier")
.help("parameters must be of the form '<param>'")
.help("identifiers cannot contain '<' or '>'")
}
Error::Uri => {
seg_span.error("component contains invalid URI characters")
.note("components cannot contain reserved characters")
.help("reserved characters include: '%', '+', '&', etc.")
}
Error::Trailing(multi) => {
let multi_span = subspan(multi, source, span);
trailspan(segment, source, span)
.error("unexpected trailing text after a '..' param")
.help("a multi-segment param must be the final component")
.span_note(multi_span, "multi-segment param is here")
}
}
}
crate fn parse_data_segment(segment: &str, span: Span) -> PResult<Segment> {
<RouteSegment<Path>>::parse_one(segment)
.map(|segment| {
let mut seg = Segment::from(segment, span);
seg.source = Source::Data;
seg.index = Some(0);
seg
})
.map_err(|e| into_diagnostic(segment, segment, span, &e))
}
crate fn parse_segments<P: UriPart>(
string: &str,
span: Span
) -> DResult<Vec<Segment>> {
let mut segments = vec![];
let mut diags = Diagnostics::new();
for result in <RouteSegment<P>>::parse_many(string) {
if let Err((segment_string, error)) = result {
diags.push(into_diagnostic(segment_string, string, span, &error));
if let Error::Trailing(..) = error {
break;
}
} else if let Ok(segment) = result {
let seg_span = subspan(&segment.string, string, span);
segments.push(Segment::from(segment, seg_span));
}
}
diags.err_or(segments)
}