summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaibo Huang <hhb@google.com>2020-07-10 20:23:22 -0700
committerChih-Hung Hsieh <chh@google.com>2020-07-13 22:16:40 -0700
commit2712e5727d219ddccacef4e2481d310ce25c7450 (patch)
tree0416dded4823e07ab57b634f1d7c3e57dde93fc3
parent21d78652b97a4994ab335c8c80f010ab792303bf (diff)
downloadpaste-impl-2712e5727d219ddccacef4e2481d310ce25c7450.tar.gz
Upgrade rust/crates/paste-impl to 0.1.18android-r-beta-3android-r-beta-2
Test: make Change-Id: Iddb09b58f336230bdaa494b0dc369096f885d510
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp9
-rw-r--r--Cargo.toml11
-rw-r--r--Cargo.toml.orig5
-rw-r--r--METADATA6
-rw-r--r--src/enum_hack.rs123
-rw-r--r--src/error.rs47
-rw-r--r--src/lib.rs375
8 files changed, 391 insertions, 187 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 6492333..77d0b53 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
{
"git": {
- "sha1": "6091cbe972d57a2a706b71da3aca82c64150ef69"
+ "sha1": "ca72ba450ad4859c5a7557371560a022649b1b1e"
}
}
diff --git a/Android.bp b/Android.bp
index deccadd..b65ed9d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5,11 +5,6 @@ rust_proc_macro {
crate_name: "paste_impl",
srcs: ["src/lib.rs"],
edition: "2018",
- rustlibs: [
- "libproc_macro2",
- "libquote",
- "libsyn",
- ],
proc_macros: [
"libproc_macro_hack",
],
@@ -17,7 +12,3 @@ rust_proc_macro {
// dependent_library ["feature_list"]
// proc-macro-hack-0.5.16
-// proc-macro2-1.0.18 "default,proc-macro"
-// quote-1.0.7 "default,proc-macro"
-// syn-1.0.33 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
-// unicode-xid-0.2.1 "default"
diff --git a/Cargo.toml b/Cargo.toml
index fa93a77..ddd3931 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
[package]
edition = "2018"
name = "paste-impl"
-version = "0.1.16"
+version = "0.1.18"
authors = ["David Tolnay <dtolnay@gmail.com>"]
description = "Implementation detail of the `paste` crate"
license = "MIT OR Apache-2.0"
@@ -25,12 +25,3 @@ targets = ["x86_64-unknown-linux-gnu"]
proc-macro = true
[dependencies.proc-macro-hack]
version = "0.5"
-
-[dependencies.proc-macro2]
-version = "1.0"
-
-[dependencies.quote]
-version = "1.0"
-
-[dependencies.syn]
-version = "1.0"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 1bc8a57..a630055 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "paste-impl"
-version = "0.1.16"
+version = "0.1.18"
authors = ["David Tolnay <dtolnay@gmail.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
@@ -12,9 +12,6 @@ proc-macro = true
[dependencies]
proc-macro-hack = "0.5"
-proc-macro2 = "1.0"
-quote = "1.0"
-syn = "1.0"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
diff --git a/METADATA b/METADATA
index ca77c1d..49cfc8f 100644
--- a/METADATA
+++ b/METADATA
@@ -9,11 +9,11 @@ third_party {
type: GIT
value: "https://github.com/dtolnay/paste"
}
- version: "0.1.16"
+ version: "0.1.18"
license_type: NOTICE
last_upgrade_date {
year: 2020
- month: 6
- day: 3
+ month: 7
+ day: 10
}
}
diff --git a/src/enum_hack.rs b/src/enum_hack.rs
index 626b265..36ab1ad 100644
--- a/src/enum_hack.rs
+++ b/src/enum_hack.rs
@@ -1,10 +1,7 @@
+use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
-
-use proc_macro2::{Ident, Span, TokenStream, TokenTree};
-use quote::quote;
-use syn::parse::{Parse, ParseStream, Result};
-use syn::{braced, parenthesized, parse_macro_input, Token};
+use std::iter::FromIterator;
pub fn wrap(output: TokenStream) -> TokenStream {
let mut hasher = DefaultHasher::default();
@@ -12,50 +9,78 @@ pub fn wrap(output: TokenStream) -> TokenStream {
let mangled_name = format!("_paste_{}", hasher.finish());
let ident = Ident::new(&mangled_name, Span::call_site());
- quote! {
- #[derive(paste::EnumHack)]
- enum #ident {
- Value = (stringify! {
- #output
- }, 0).1,
- }
- }
-}
-
-struct EnumHack {
- token_stream: TokenStream,
-}
-
-impl Parse for EnumHack {
- fn parse(input: ParseStream) -> Result<Self> {
- input.parse::<Token![enum]>()?;
- input.parse::<Ident>()?;
-
- let braces;
- braced!(braces in input);
- braces.parse::<Ident>()?;
- braces.parse::<Token![=]>()?;
-
- let parens;
- parenthesized!(parens in braces);
- parens.parse::<Ident>()?;
- parens.parse::<Token![!]>()?;
-
- let inner;
- braced!(inner in parens);
- let token_stream: TokenStream = inner.parse()?;
-
- parens.parse::<Token![,]>()?;
- parens.parse::<TokenTree>()?;
- braces.parse::<Token![.]>()?;
- braces.parse::<TokenTree>()?;
- braces.parse::<Token![,]>()?;
-
- Ok(EnumHack { token_stream })
- }
+ // #[derive(paste::EnumHack)]
+ // enum #ident {
+ // Value = (stringify! {
+ // #output
+ // }, 0).1,
+ // }
+ TokenStream::from_iter(vec![
+ TokenTree::Punct(Punct::new('#', Spacing::Alone)),
+ TokenTree::Group(Group::new(
+ Delimiter::Bracket,
+ TokenStream::from_iter(vec![
+ TokenTree::Ident(Ident::new("derive", Span::call_site())),
+ TokenTree::Group(Group::new(
+ Delimiter::Parenthesis,
+ TokenStream::from_iter(vec![
+ TokenTree::Ident(Ident::new("paste", Span::call_site())),
+ TokenTree::Punct(Punct::new(':', Spacing::Joint)),
+ TokenTree::Punct(Punct::new(':', Spacing::Alone)),
+ TokenTree::Ident(Ident::new("EnumHack", Span::call_site())),
+ ]),
+ )),
+ ]),
+ )),
+ TokenTree::Ident(Ident::new("enum", Span::call_site())),
+ TokenTree::Ident(ident),
+ TokenTree::Group(Group::new(
+ Delimiter::Brace,
+ TokenStream::from_iter(vec![
+ TokenTree::Ident(Ident::new("Value", Span::call_site())),
+ TokenTree::Punct(Punct::new('=', Spacing::Alone)),
+ TokenTree::Group(Group::new(
+ Delimiter::Parenthesis,
+ TokenStream::from_iter(vec![
+ TokenTree::Ident(Ident::new("stringify", Span::call_site())),
+ TokenTree::Punct(Punct::new('!', Spacing::Alone)),
+ TokenTree::Group(Group::new(Delimiter::Brace, output)),
+ TokenTree::Punct(Punct::new(',', Spacing::Alone)),
+ TokenTree::Literal(Literal::usize_unsuffixed(0)),
+ ]),
+ )),
+ TokenTree::Punct(Punct::new('.', Spacing::Alone)),
+ TokenTree::Literal(Literal::usize_unsuffixed(1)),
+ TokenTree::Punct(Punct::new(',', Spacing::Alone)),
+ ]),
+ )),
+ ])
}
-pub fn extract(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
- let inner = parse_macro_input!(input as EnumHack);
- proc_macro::TokenStream::from(inner.token_stream)
+pub fn extract(input: TokenStream) -> TokenStream {
+ let mut tokens = input.into_iter();
+ let _ = tokens.next().expect("enum");
+ let _ = tokens.next().expect("#ident");
+ let mut braces = match tokens.next().expect("{...}") {
+ TokenTree::Group(group) => group.stream().into_iter(),
+ _ => unreachable!("{...}"),
+ };
+ let _ = braces.next().expect("Value");
+ let _ = braces.next().expect("=");
+ let mut parens = match braces.next().expect("(...)") {
+ TokenTree::Group(group) => group.stream().into_iter(),
+ _ => unreachable!("(...)"),
+ };
+ let _ = parens.next().expect("stringify");
+ let _ = parens.next().expect("!");
+ let token_stream = match parens.next().expect("{...}") {
+ TokenTree::Group(group) => group.stream(),
+ _ => unreachable!("{...}"),
+ };
+ let _ = parens.next().expect(",");
+ let _ = parens.next().expect("0");
+ let _ = braces.next().expect(".");
+ let _ = braces.next().expect("1");
+ let _ = braces.next().expect(",");
+ token_stream
}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..7c5badb
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,47 @@
+use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
+use std::iter::FromIterator;
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+pub struct Error {
+ begin: Span,
+ end: Span,
+ msg: String,
+}
+
+impl Error {
+ pub fn new(span: Span, msg: &str) -> Self {
+ Self::new2(span, span, msg)
+ }
+
+ pub fn new2(begin: Span, end: Span, msg: &str) -> Self {
+ Error {
+ begin,
+ end,
+ msg: msg.to_owned(),
+ }
+ }
+
+ pub fn to_compile_error(&self) -> TokenStream {
+ // compile_error! { $msg }
+ TokenStream::from_iter(vec![
+ TokenTree::Ident(Ident::new("compile_error", self.begin)),
+ TokenTree::Punct({
+ let mut punct = Punct::new('!', Spacing::Alone);
+ punct.set_span(self.begin);
+ punct
+ }),
+ TokenTree::Group({
+ let mut group = Group::new(Delimiter::Brace, {
+ TokenStream::from_iter(vec![TokenTree::Literal({
+ let mut string = Literal::string(&self.msg);
+ string.set_span(self.end);
+ string
+ })])
+ });
+ group.set_span(self.end);
+ group
+ }),
+ ])
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 2feee4c..f84715c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,73 +1,84 @@
extern crate proc_macro;
mod enum_hack;
+mod error;
-use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
+use crate::error::{Error, Result};
+use proc_macro::{
+ token_stream, Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree,
+};
use proc_macro_hack::proc_macro_hack;
-use quote::{quote, ToTokens};
-use std::iter::FromIterator;
-use syn::parse::{Error, Parse, ParseStream, Parser, Result};
-use syn::{parenthesized, parse_macro_input, Lit, LitStr, Token};
+use std::iter::{self, FromIterator, Peekable};
+use std::panic;
#[proc_macro]
-pub fn item(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
- let input = parse_macro_input!(input as PasteInput);
- proc_macro::TokenStream::from(input.expanded)
+pub fn item(input: TokenStream) -> TokenStream {
+ expand_paste(input)
}
#[proc_macro]
-pub fn item_with_macros(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
- let input = parse_macro_input!(input as PasteInput);
- proc_macro::TokenStream::from(enum_hack::wrap(input.expanded))
+pub fn item_with_macros(input: TokenStream) -> TokenStream {
+ enum_hack::wrap(expand_paste(input))
}
#[proc_macro_hack]
-pub fn expr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
- let input = parse_macro_input!(input as PasteInput);
- let output = input.expanded;
- proc_macro::TokenStream::from(quote!({ #output }))
+pub fn expr(input: TokenStream) -> TokenStream {
+ TokenStream::from(TokenTree::Group(Group::new(
+ Delimiter::Brace,
+ expand_paste(input),
+ )))
}
#[doc(hidden)]
#[proc_macro_derive(EnumHack)]
-pub fn enum_hack(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+pub fn enum_hack(input: TokenStream) -> TokenStream {
enum_hack::extract(input)
}
-struct PasteInput {
- expanded: TokenStream,
-}
-
-impl Parse for PasteInput {
- fn parse(input: ParseStream) -> Result<Self> {
- let mut contains_paste = false;
- let expanded = parse(input, &mut contains_paste)?;
- Ok(PasteInput { expanded })
+fn expand_paste(input: TokenStream) -> TokenStream {
+ let mut contains_paste = false;
+ match expand(input, &mut contains_paste) {
+ Ok(expanded) => expanded,
+ Err(err) => err.to_compile_error(),
}
}
-fn parse(input: ParseStream, contains_paste: &mut bool) -> Result<TokenStream> {
+fn expand(input: TokenStream, contains_paste: &mut bool) -> Result<TokenStream> {
let mut expanded = TokenStream::new();
- let (mut prev_colons, mut colons) = (false, false);
- while !input.is_empty() {
- let save = input.fork();
- match input.parse()? {
- TokenTree::Group(group) => {
+ let (mut prev_colon, mut colon) = (false, false);
+ let mut prev_none_group = None::<Group>;
+ let mut tokens = input.into_iter().peekable();
+ loop {
+ let token = tokens.next();
+ if let Some(group) = prev_none_group.take() {
+ if match (&token, tokens.peek()) {
+ (Some(TokenTree::Punct(fst)), Some(TokenTree::Punct(snd))) => {
+ fst.as_char() == ':' && snd.as_char() == ':' && fst.spacing() == Spacing::Joint
+ }
+ _ => false,
+ } {
+ expanded.extend(group.stream());
+ *contains_paste = true;
+ } else {
+ expanded.extend(iter::once(TokenTree::Group(group)));
+ }
+ }
+ match token {
+ Some(TokenTree::Group(group)) => {
let delimiter = group.delimiter();
let content = group.stream();
let span = group.span();
if delimiter == Delimiter::Bracket && is_paste_operation(&content) {
- let segments = parse_bracket_as_segments.parse2(content)?;
+ let segments = parse_bracket_as_segments(content, span)?;
let pasted = paste_segments(span, &segments)?;
- pasted.to_tokens(&mut expanded);
+ expanded.extend(pasted);
*contains_paste = true;
} else if is_none_delimited_flat_group(delimiter, &content) {
- content.to_tokens(&mut expanded);
+ expanded.extend(content);
*contains_paste = true;
} else {
let mut group_contains_paste = false;
- let nested = (|input: ParseStream| parse(input, &mut group_contains_paste))
- .parse2(content)?;
+ let nested = expand(content, &mut group_contains_paste)?;
let group = if group_contains_paste {
let mut group = Group::new(delimiter, nested);
group.set_span(span);
@@ -76,26 +87,34 @@ fn parse(input: ParseStream, contains_paste: &mut bool) -> Result<TokenStream> {
} else {
group.clone()
};
- let in_path = prev_colons || input.peek(Token![::]);
- if in_path && delimiter == Delimiter::None {
- group.stream().to_tokens(&mut expanded);
+ if delimiter != Delimiter::None {
+ expanded.extend(iter::once(TokenTree::Group(group)));
+ } else if prev_colon {
+ expanded.extend(group.stream());
*contains_paste = true;
} else {
- group.to_tokens(&mut expanded);
+ prev_none_group = Some(group);
+ }
+ }
+ prev_colon = false;
+ colon = false;
+ }
+ Some(other) => {
+ match &other {
+ TokenTree::Punct(punct) if punct.as_char() == ':' => {
+ prev_colon = colon;
+ colon = punct.spacing() == Spacing::Joint;
+ }
+ _ => {
+ prev_colon = false;
+ colon = false;
}
}
+ expanded.extend(iter::once(other));
}
- other => other.to_tokens(&mut expanded),
+ None => return Ok(expanded),
}
- prev_colons = colons;
- colons = save.peek(Token![::]);
}
- Ok(expanded)
-}
-
-fn is_paste_operation(input: &TokenStream) -> bool {
- let input = input.clone();
- parse_bracket_as_segments.parse2(input).is_ok()
}
// https://github.com/dtolnay/paste/issues/26
@@ -140,61 +159,173 @@ fn is_none_delimited_flat_group(delimiter: Delimiter, input: &TokenStream) -> bo
state == State::Ident || state == State::Literal || state == State::Lifetime
}
+struct LitStr {
+ value: String,
+ span: Span,
+}
+
+struct Colon {
+ span: Span,
+}
+
enum Segment {
String(String),
Apostrophe(Span),
Env(LitStr),
- Modifier(Token![:], Ident),
+ Modifier(Colon, Ident),
}
-fn parse_bracket_as_segments(input: ParseStream) -> Result<Vec<Segment>> {
- input.parse::<Token![<]>()?;
+fn is_paste_operation(input: &TokenStream) -> bool {
+ let mut tokens = input.clone().into_iter();
- let segments = parse_segments(input)?;
+ match &tokens.next() {
+ Some(TokenTree::Punct(punct)) if punct.as_char() == '<' => {}
+ _ => return false,
+ }
- input.parse::<Token![>]>()?;
- if !input.is_empty() {
- return Err(input.error("invalid input"));
+ let mut has_token = false;
+ loop {
+ match &tokens.next() {
+ Some(TokenTree::Punct(punct)) if punct.as_char() == '>' => {
+ return has_token && tokens.next().is_none();
+ }
+ Some(_) => has_token = true,
+ None => return false,
+ }
}
- Ok(segments)
}
-fn parse_segments(input: ParseStream) -> Result<Vec<Segment>> {
+fn parse_bracket_as_segments(input: TokenStream, scope: Span) -> Result<Vec<Segment>> {
+ let mut tokens = input.into_iter().peekable();
+
+ match &tokens.next() {
+ Some(TokenTree::Punct(punct)) if punct.as_char() == '<' => {}
+ Some(wrong) => return Err(Error::new(wrong.span(), "expected `<`")),
+ None => return Err(Error::new(scope, "expected `[< ... >]`")),
+ }
+
+ let segments = parse_segments(&mut tokens, scope)?;
+
+ match &tokens.next() {
+ Some(TokenTree::Punct(punct)) if punct.as_char() == '>' => {}
+ Some(wrong) => return Err(Error::new(wrong.span(), "expected `>`")),
+ None => return Err(Error::new(scope, "expected `[< ... >]`")),
+ }
+
+ match tokens.next() {
+ Some(unexpected) => Err(Error::new(
+ unexpected.span(),
+ "unexpected input, expected `[< ... >]`",
+ )),
+ None => Ok(segments),
+ }
+}
+
+fn parse_segments(
+ tokens: &mut Peekable<token_stream::IntoIter>,
+ scope: Span,
+) -> Result<Vec<Segment>> {
let mut segments = Vec::new();
- while !(input.is_empty() || input.peek(Token![>])) {
- match input.parse()? {
+ while match tokens.peek() {
+ None => false,
+ Some(TokenTree::Punct(punct)) => punct.as_char() != '>',
+ Some(_) => true,
+ } {
+ match tokens.next().unwrap() {
TokenTree::Ident(ident) => {
let mut fragment = ident.to_string();
if fragment.starts_with("r#") {
fragment = fragment.split_off(2);
}
- if fragment == "env" && input.peek(Token![!]) {
- input.parse::<Token![!]>()?;
- let arg;
- parenthesized!(arg in input);
- let var: LitStr = arg.parse()?;
- segments.push(Segment::Env(var));
+ if fragment == "env"
+ && match tokens.peek() {
+ Some(TokenTree::Punct(punct)) => punct.as_char() == '!',
+ _ => false,
+ }
+ {
+ tokens.next().unwrap(); // `!`
+ let expect_group = tokens.next();
+ let parenthesized = match &expect_group {
+ Some(TokenTree::Group(group))
+ if group.delimiter() == Delimiter::Parenthesis =>
+ {
+ group
+ }
+ Some(wrong) => return Err(Error::new(wrong.span(), "expected `(`")),
+ None => return Err(Error::new(scope, "expected `(` after `env!`")),
+ };
+ let mut inner = parenthesized.stream().into_iter();
+ let lit = match inner.next() {
+ Some(TokenTree::Literal(lit)) => lit,
+ Some(wrong) => {
+ return Err(Error::new(wrong.span(), "expected string literal"))
+ }
+ None => {
+ return Err(Error::new2(
+ ident.span(),
+ parenthesized.span(),
+ "expected string literal as argument to env! macro",
+ ))
+ }
+ };
+ let lit_string = lit.to_string();
+ if lit_string.starts_with('"')
+ && lit_string.ends_with('"')
+ && lit_string.len() >= 2
+ {
+ // TODO: maybe handle escape sequences in the string if
+ // someone has a use case.
+ segments.push(Segment::Env(LitStr {
+ value: lit_string[1..lit_string.len() - 1].to_owned(),
+ span: lit.span(),
+ }));
+ } else {
+ return Err(Error::new(lit.span(), "expected string literal"));
+ }
+ if let Some(unexpected) = inner.next() {
+ return Err(Error::new(
+ unexpected.span(),
+ "unexpected token in env! macro",
+ ));
+ }
} else {
segments.push(Segment::String(fragment));
}
}
TokenTree::Literal(lit) => {
- let value = match syn::parse_str(&lit.to_string())? {
- Lit::Str(string) => string.value().replace('-', "_"),
- Lit::Int(_) => lit.to_string(),
- _ => return Err(Error::new(lit.span(), "unsupported literal")),
- };
- segments.push(Segment::String(value));
+ let mut lit_string = lit.to_string();
+ if lit_string.contains(&['#', '\\', '.', '+'][..]) {
+ return Err(Error::new(lit.span(), "unsupported literal"));
+ }
+ lit_string = lit_string
+ .replace('"', "")
+ .replace('\'', "")
+ .replace('-', "_");
+ segments.push(Segment::String(lit_string));
}
TokenTree::Punct(punct) => match punct.as_char() {
- '_' => segments.push(Segment::String("_".to_string())),
+ '_' => segments.push(Segment::String("_".to_owned())),
'\'' => segments.push(Segment::Apostrophe(punct.span())),
- ':' => segments.push(Segment::Modifier(Token![:](punct.span()), input.parse()?)),
+ ':' => {
+ let colon = Colon { span: punct.span() };
+ let ident = match tokens.next() {
+ Some(TokenTree::Ident(ident)) => ident,
+ wrong => {
+ let span = wrong.as_ref().map_or(scope, TokenTree::span);
+ return Err(Error::new(span, "expected identifier after `:`"));
+ }
+ };
+ segments.push(Segment::Modifier(colon, ident));
+ }
_ => return Err(Error::new(punct.span(), "unexpected punct")),
},
TokenTree::Group(group) => {
if group.delimiter() == Delimiter::None {
- let nested = parse_segments.parse2(group.stream())?;
+ let mut inner = group.stream().into_iter().peekable();
+ let nested = parse_segments(&mut inner, group.span())?;
+ if let Some(unexpected) = inner.next() {
+ return Err(Error::new(unexpected.span(), "unexpected token"));
+ }
segments.extend(nested);
} else {
return Err(Error::new(group.span(), "unexpected token"));
@@ -221,65 +352,87 @@ fn paste_segments(span: Span, segments: &[Segment]) -> Result<TokenStream> {
is_lifetime = true;
}
Segment::Env(var) => {
- let resolved = match std::env::var(var.value()) {
+ let resolved = match std::env::var(&var.value) {
Ok(resolved) => resolved,
Err(_) => {
- return Err(Error::new(var.span(), "no such env var"));
+ return Err(Error::new(
+ var.span,
+ &format!("no such env var: {:?}", var.value),
+ ));
}
};
let resolved = resolved.replace('-', "_");
evaluated.push(resolved);
}
Segment::Modifier(colon, ident) => {
- let span = quote!(#colon #ident);
let last = match evaluated.pop() {
Some(last) => last,
- None => return Err(Error::new_spanned(span, "unexpected modifier")),
+ None => {
+ return Err(Error::new2(colon.span, ident.span(), "unexpected modifier"))
+ }
};
- if ident == "lower" {
- evaluated.push(last.to_lowercase());
- } else if ident == "upper" {
- evaluated.push(last.to_uppercase());
- } else if ident == "snake" {
- let mut acc = String::new();
- let mut prev = '_';
- for ch in last.chars() {
- if ch.is_uppercase() && prev != '_' {
- acc.push('_');
+ match ident.to_string().as_str() {
+ "lower" => {
+ evaluated.push(last.to_lowercase());
+ }
+ "upper" => {
+ evaluated.push(last.to_uppercase());
+ }
+ "snake" => {
+ let mut acc = String::new();
+ let mut prev = '_';
+ for ch in last.chars() {
+ if ch.is_uppercase() && prev != '_' {
+ acc.push('_');
+ }
+ acc.push(ch);
+ prev = ch;
}
- acc.push(ch);
- prev = ch;
+ evaluated.push(acc.to_lowercase());
}
- evaluated.push(acc.to_lowercase());
- } else if ident == "camel" {
- let mut acc = String::new();
- let mut prev = '_';
- for ch in last.chars() {
- if ch != '_' {
- if prev == '_' {
- for chu in ch.to_uppercase() {
- acc.push(chu);
- }
- } else if prev.is_uppercase() {
- for chl in ch.to_lowercase() {
- acc.push(chl);
+ "camel" => {
+ let mut acc = String::new();
+ let mut prev = '_';
+ for ch in last.chars() {
+ if ch != '_' {
+ if prev == '_' {
+ for chu in ch.to_uppercase() {
+ acc.push(chu);
+ }
+ } else if prev.is_uppercase() {
+ for chl in ch.to_lowercase() {
+ acc.push(chl);
+ }
+ } else {
+ acc.push(ch);
}
- } else {
- acc.push(ch);
}
+ prev = ch;
}
- prev = ch;
+ evaluated.push(acc);
+ }
+ _ => {
+ return Err(Error::new2(
+ colon.span,
+ ident.span(),
+ "unsupported modifier",
+ ));
}
- evaluated.push(acc);
- } else {
- return Err(Error::new_spanned(span, "unsupported modifier"));
}
}
}
}
let pasted = evaluated.into_iter().collect::<String>();
- let ident = TokenTree::Ident(Ident::new(&pasted, span));
+ let ident = match panic::catch_unwind(|| Ident::new(&pasted, span)) {
+ Ok(ident) => TokenTree::Ident(ident),
+ Err(_) => {
+ return Err(Error::new(
+ span,
+ &format!("`{:?}` is not a valid identifier", pasted),
+ ));
+ }
+ };
let tokens = if is_lifetime {
let apostrophe = TokenTree::Punct(Punct::new('\'', Spacing::Joint));
vec![apostrophe, ident]