mirror of
https://github.com/rwf2/Rocket.git
synced 2025-02-09 10:12:02 +00:00
150 lines
4.6 KiB
Rust
150 lines
4.6 KiB
Rust
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, // The segment that failed.
|
|
source: &str, // The haystack where `segment` can be found.
|
|
span: Span, // The `Span` of `Source`.
|
|
error: &Error, // The 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)
|
|
}
|