sscanf_macro/
utils.rs

1#![allow(unused)]
2
3use crate::*;
4
5/// A workaround for Spans on stable Rust.
6///
7/// Span manipulation doesn't work on stable Rust, which also means that spans cannot be joined
8/// together. This means that any compiler errors that occur would only point at the first token
9/// of the spanned expression, which is not very helpful.
10///
11/// The workaround, as demonstrated by `syn::Error::new_spanned`, is to have the first part of the
12/// spanned expression be spanned with the first part of the source span, and the second part of the
13/// spanned expression be spanned with the second part of the source span. The compiler only looks
14/// at the start and end of the span and underlines everything in between, so this works.
15#[derive(Copy, Clone)]
16pub struct FullSpan(Span, Span);
17
18impl FullSpan {
19    pub fn from_span(span: Span) -> Self {
20        Self(span, span)
21    }
22    pub fn from_spanned<T: ToTokens + syn::spanned::Spanned>(span: &T) -> Self {
23        let start = span.span();
24        let end = span
25            .to_token_stream()
26            .into_iter()
27            .last()
28            .map(|t| t.span())
29            .unwrap_or(start);
30        Self(start, end)
31    }
32    pub fn apply(self, a: TokenStream, b: TokenStream) -> TokenStream {
33        let mut ret = self.apply_start(a);
34        ret.extend(self.apply_end(b));
35        ret
36    }
37    pub fn apply_start(self, a: TokenStream) -> TokenStream {
38        a.with_span(self.0)
39    }
40    pub fn apply_end(self, b: TokenStream) -> TokenStream {
41        b.with_span(self.1)
42    }
43}
44
45/// Find the closest match to a string in a list of strings.
46pub fn find_closest<'a>(s: &str, compare: &[&'a str]) -> Option<&'a str> {
47    let mut best_confidence = 0.8; // minimum confidence
48    let mut best_match = None;
49    for valid in compare {
50        let confidence = strsim::jaro_winkler(s, valid);
51        if confidence > best_confidence {
52            best_confidence = confidence;
53            best_match = Some(*valid);
54        }
55    }
56    best_match
57}
58
59/// Format a list of items as a comma-separated list, with "or" before the last item.
60pub fn list_items<T>(items: &[T], mut display: impl FnMut(&T) -> String) -> String {
61    match items {
62        [] => String::new(),
63        [x] => display(x),
64        [a, b] => format!("{} or {}", display(a), display(b)),
65        [start @ .., last] => {
66            let mut s = String::new();
67            for item in start {
68                s += &display(item);
69                s += ", ";
70            }
71            s += "or ";
72            s += &display(last);
73            s
74        }
75    }
76}
77
78/// Extension trait for [`TokenStream`] that allows setting the span of all tokens in the stream.
79pub trait TokenStreamExt {
80    fn set_span(&mut self, span: Span);
81    fn with_span(self, span: Span) -> Self;
82}
83impl TokenStreamExt for TokenStream {
84    fn set_span(&mut self, span: Span) {
85        let old = std::mem::replace(self, TokenStream::new());
86        *self = old.with_span(span);
87    }
88    fn with_span(self, span: Span) -> Self {
89        self.into_iter()
90            .map(|mut t| {
91                if let proc_macro2::TokenTree::Group(ref mut g) = t {
92                    *g = proc_macro2::Group::new(g.delimiter(), g.stream().with_span(span));
93                }
94                t.set_span(span);
95                t
96            })
97            .collect()
98    }
99}