summaryrefslogtreecommitdiffstats
path: root/vendor/xshell-macros
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /vendor/xshell-macros
parentInitial commit. (diff)
downloadrustc-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.json1
-rw-r--r--vendor/xshell-macros/Cargo.toml24
-rw-r--r--vendor/xshell-macros/src/lib.rs203
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()
+}