diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /vendor/xshell-macros | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/xshell-macros')
-rw-r--r-- | vendor/xshell-macros/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | vendor/xshell-macros/Cargo.toml | 24 | ||||
-rw-r--r-- | vendor/xshell-macros/src/lib.rs | 203 |
3 files changed, 228 insertions, 0 deletions
diff --git a/vendor/xshell-macros/.cargo-checksum.json b/vendor/xshell-macros/.cargo-checksum.json new file mode 100644 index 000000000..5198d9797 --- /dev/null +++ b/vendor/xshell-macros/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"4c4a333e83824134161380aa9efb26335313dbca9f09e6a7160d53ce4c2f6918","src/lib.rs":"d91daf4306a265ab7343600f8bab807e059de30c04b9e0494faec45404a7efaa"},"package":"88301b56c26dd9bf5c43d858538f82d6f3f7764767defbc5d34e59459901c41a"}
\ No newline at end of file diff --git a/vendor/xshell-macros/Cargo.toml b/vendor/xshell-macros/Cargo.toml new file mode 100644 index 000000000..c95f0be33 --- /dev/null +++ b/vendor/xshell-macros/Cargo.toml @@ -0,0 +1,24 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.59" +name = "xshell-macros" +version = "0.2.2" +authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"] +description = "Private implementation detail of xshell crate" +license = "MIT OR Apache-2.0" +repository = "https://github.com/matklad/xshell" +resolver = "2" + +[lib] +proc-macro = true diff --git a/vendor/xshell-macros/src/lib.rs b/vendor/xshell-macros/src/lib.rs new file mode 100644 index 000000000..e98c16a90 --- /dev/null +++ b/vendor/xshell-macros/src/lib.rs @@ -0,0 +1,203 @@ +//! Private implementation details of `xshell`. + +#![deny(missing_debug_implementations)] +#![deny(rust_2018_idioms)] + +use std::iter; + +use proc_macro::{Delimiter, Group, Literal, Span, TokenStream, TokenTree}; + +#[doc(hidden)] +#[proc_macro] +pub fn __cmd(macro_arg: TokenStream) -> TokenStream { + try_cmd(macro_arg).unwrap_or_else(|msg| parse_ts(&format!("compile_error!({:?})", msg))) +} + +type Result<T> = std::result::Result<T, String>; + +fn try_cmd(macro_arg: TokenStream) -> Result<TokenStream> { + let (cmd, literal) = { + let mut iter = macro_arg.into_iter(); + let cmd = iter.next().unwrap(); + let literal = iter.next().unwrap(); + assert!(iter.next().is_none()); + (cmd, literal) + }; + + let literal = match into_literal(&literal) { + Some(it) => it, + None => return Err("expected a plain string literal".to_string()), + }; + + let literal_text = literal.to_string(); + if !literal_text.starts_with('"') { + return Err("expected a plain string literal".to_string()); + } + + let mut args = shell_lex(literal_text.as_str(), literal.span()); + + let mut res = TokenStream::new(); + + { + let (_joined_to_prev, splat, program) = + args.next().ok_or_else(|| "command can't be empty".to_string())??; + if splat { + return Err("can't splat program name".to_string()); + } + res.extend(Some(cmd)); + res.extend(program); + } + + let mut prev_spat = false; + for arg in args { + let (joined_to_prev, splat, arg) = arg?; + if prev_spat && joined_to_prev { + return Err(format!( + "can't combine splat with concatenation, add spaces around `{{{}...}}`", + trim_decorations(&res.into_iter().last().unwrap().to_string()), + )); + } + prev_spat = splat; + + let method = match (joined_to_prev, splat) { + (false, false) => ".arg", + (false, true) => ".args", + (true, false) => ".__extend_arg", + (true, true) => { + return Err(format!( + "can't combine splat with concatenation, add spaces around `{{{}...}}`", + trim_decorations(&arg.to_string()), + )) + } + }; + + res.extend(parse_ts(method)); + res.extend(arg); + } + + Ok(res) +} + +fn into_literal(ts: &TokenTree) -> Option<Literal> { + match ts { + TokenTree::Literal(l) => Some(l.clone()), + TokenTree::Group(g) => match g.delimiter() { + Delimiter::None => match g.stream().into_iter().collect::<Vec<_>>().as_slice() { + [TokenTree::Literal(l)] => Some(l.clone()), + _ => None, + }, + Delimiter::Parenthesis | Delimiter::Brace | Delimiter::Bracket => None, + }, + _ => None, + } +} + +fn trim_decorations(s: &str) -> &str { + &s[1..s.len() - 1] +} + +fn shell_lex( + cmd: &str, + call_site: Span, +) -> impl Iterator<Item = Result<(bool, bool, TokenStream)>> + '_ { + tokenize(cmd).map(move |token| { + let token = token?; + let mut splat = false; + let ts = match token.kind { + TokenKind::Word => parse_ts(&format!("(\"{}\")", token.text)), + TokenKind::String => parse_ts(&format!("(\"{}\")", trim_decorations(token.text))), + TokenKind::Interpolation { splat: s } => { + splat = s; + let text = trim_decorations(token.text); + let text = &text[..text.len() - (if splat { "...".len() } else { 0 })]; + if !(text.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')) { + return Err(format!( + "can only interpolate simple variables, got this expression instead: `{}`", + text + )); + } + let ts = if splat { format!("({})", text) } else { format!("(&({}))", text) }; + respan(parse_ts(&ts), call_site) + } + }; + Ok((token.joined_to_prev, splat, ts)) + }) +} + +/// Like trim_matches except only trims a maximum of 1 match +fn strip_matches<'a>(s: &'a str, pattern: &str) -> &'a str { + s.strip_prefix(pattern).unwrap_or(s).strip_suffix(pattern).unwrap_or(s) +} + +fn tokenize(cmd: &str) -> impl Iterator<Item = Result<Token<'_>>> + '_ { + let mut cmd = strip_matches(cmd, "\""); + + iter::from_fn(move || { + let old_len = cmd.len(); + cmd = cmd.trim_start(); + let joined_to_prev = old_len == cmd.len(); + if cmd.is_empty() { + return None; + } + let (len, kind) = match next_token(cmd) { + Ok(it) => it, + Err(err) => { + cmd = ""; + return Some(Err(err)); + } + }; + let token = Token { joined_to_prev, text: &cmd[..len], kind }; + cmd = &cmd[len..]; + Some(Ok(token)) + }) +} + +#[derive(Debug)] +struct Token<'a> { + joined_to_prev: bool, + text: &'a str, + kind: TokenKind, +} +#[derive(Debug)] +enum TokenKind { + Word, + String, + Interpolation { splat: bool }, +} + +fn next_token(s: &str) -> Result<(usize, TokenKind)> { + if s.starts_with('{') { + let len = s.find('}').ok_or_else(|| "unclosed `{` in command".to_string())? + 1; + let splat = s[..len].ends_with("...}"); + return Ok((len, TokenKind::Interpolation { splat })); + } + if s.starts_with('\'') { + let len = s[1..].find('\'').ok_or_else(|| "unclosed `'` in command".to_string())? + 2; + return Ok((len, TokenKind::String)); + } + let len = + s.find(|it: char| it.is_ascii_whitespace() || it == '\'' || it == '{').unwrap_or(s.len()); + Ok((len, TokenKind::Word)) +} + +fn respan(ts: TokenStream, span: Span) -> TokenStream { + let mut res = TokenStream::new(); + for tt in ts { + let tt = match tt { + TokenTree::Ident(mut ident) => { + ident.set_span(ident.span().resolved_at(span).located_at(span)); + TokenTree::Ident(ident) + } + TokenTree::Group(group) => { + TokenTree::Group(Group::new(group.delimiter(), respan(group.stream(), span))) + } + _ => tt, + }; + res.extend(Some(tt)) + } + res +} + +fn parse_ts(s: &str) -> TokenStream { + s.parse().unwrap() +} |