pendzl_lang_codegen/
internal.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2012-2022 Supercolony
3//
4// Permission is hereby granted, free of charge, to any person obtaining
5// a copy of this software and associated documentation files (the"Software"),
6// to deal in the Software without restriction, including
7// without limitation the rights to use, copy, modify, merge, publish,
8// distribute, sublicense, and/or sell copies of the Software, and to
9// permit persons to whom the Software is furnished to do so, subject to
10// the following conditions:
11//
12// The above copyright notice and this permission notice shall be
13// included in all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23extern crate proc_macro;
24
25use quote::ToTokens;
26use std::collections::HashSet;
27use syn::{
28    ext::IdentExt,
29    parenthesized,
30    parse::{Parse, ParseStream},
31    punctuated::Punctuated,
32    token::Comma,
33    FnArg,
34};
35
36pub(crate) struct MetaList {
37    pub _path: syn::Path,
38    pub _paren_token: syn::token::Paren,
39    pub _nested: syn::punctuated::Punctuated<syn::Expr, syn::Token![,]>,
40}
41#[derive(Debug)]
42pub struct InputsDiff {
43    pub added: HashSet<String>,
44    pub removed: HashSet<String>,
45}
46
47pub fn inputs_diff(
48    inputs_a: Punctuated<FnArg, Comma>,
49    inputs_b: Punctuated<FnArg, Comma>,
50) -> InputsDiff {
51    let set_a: HashSet<_> = inputs_a
52        .into_iter()
53        .map(|arg| arg.into_token_stream().to_string())
54        .collect();
55    let set_b: HashSet<_> = inputs_b
56        .into_iter()
57        .map(|arg| arg.into_token_stream().to_string())
58        .collect();
59
60    let added = &set_b - &set_a;
61    let removed = &set_a - &set_b;
62
63    InputsDiff {
64        added: added.into_iter().collect(),
65        removed: removed.into_iter().collect(),
66    }
67}
68
69pub fn format_arg_string(arg: &str) -> String {
70    let mut formatted = String::new();
71    let mut chars = arg.chars().peekable();
72
73    while let Some(ch) = chars.next() {
74
75        match ch {
76            '&' => {
77                if chars.peek() == Some(&' ') {
78                    formatted.push('&');
79                    chars.next(); // Remove space after '&'
80                }
81            }
82            _ => {
83                if chars.peek() == Some(&':') {
84                    formatted.push(':');
85                    chars.next();
86                }
87                else if chars.peek() == Some(&'>') {
88                    formatted.push('>');
89                    chars.next();
90                    chars.next();
91                }
92                else if chars.peek() == Some(&'<') {
93                    formatted.push('<');
94                    chars.next();
95                    chars.next();
96                }
97                else if chars.peek() == Some(&',') {
98                    formatted.push(',');
99                    chars.next();
100                }else {
101                    formatted.push(ch);
102                }
103            }
104        }
105    }
106
107    formatted
108}
109
110// Like Path::parse_mod_style but accepts keywords in the path.
111fn parse_meta_path(input: ParseStream) -> syn::Result<syn::Path> {
112    Ok(syn::Path {
113        leading_colon: input.parse()?,
114        segments: {
115            let mut segments = syn::punctuated::Punctuated::new();
116            while input.peek(syn::Ident::peek_any) {
117                let ident = syn::Ident::parse_any(input)?;
118                segments.push_value(syn::PathSegment::from(ident));
119                if !input.peek(syn::Token![::]) {
120                    break;
121                }
122                let punct = input.parse()?;
123                segments.push_punct(punct);
124            }
125            if segments.is_empty() {
126                return Err(input.error("expected path"));
127            } else if segments.trailing_punct() {
128                return Err(input.error("expected path segment"));
129            }
130            segments
131        },
132    })
133}
134
135fn parse_meta_list_after_path(path: syn::Path, input: ParseStream) -> syn::Result<MetaList> {
136    let content;
137    Ok(MetaList {
138        _path: path,
139        _paren_token: parenthesized!(content in input),
140        _nested: content.parse_terminated(syn::Expr::parse)?,
141    })
142}
143
144fn parse_meta_after_path(path: syn::Path, input: ParseStream) -> syn::Result<NestedMeta> {
145    if input.peek(syn::token::Paren) {
146        parse_meta_list_after_path(path, input).map(NestedMeta::List)
147    } else {
148        Ok(NestedMeta::Path(path))
149    }
150}
151
152impl Parse for MetaList {
153    fn parse(input: ParseStream) -> syn::Result<Self> {
154        let path = input.call(parse_meta_path)?;
155        parse_meta_list_after_path(path, input)
156    }
157}
158
159pub(crate) enum NestedMeta {
160    Path(syn::Path),
161    List(MetaList),
162}
163
164impl Parse for NestedMeta {
165    fn parse(input: ParseStream) -> syn::Result<Self> {
166        let path = input.call(parse_meta_path)?;
167        parse_meta_after_path(path, input)
168    }
169}
170
171pub(crate) struct AttributeArgs(Vec<NestedMeta>);
172
173impl Parse for AttributeArgs {
174    fn parse(input: ParseStream) -> syn::Result<Self> {
175        let mut attrs = Vec::new();
176        while input.peek(syn::Ident::peek_any) {
177            attrs.push(input.parse()?);
178            if input.is_empty() {
179                break;
180            }
181            let _: syn::token::Comma = input.parse()?;
182        }
183        Ok(AttributeArgs(attrs))
184    }
185}
186
187impl std::ops::Deref for AttributeArgs {
188    type Target = Vec<NestedMeta>;
189
190    fn deref(&self) -> &Self::Target {
191        &self.0
192    }
193}
194
195impl std::ops::DerefMut for AttributeArgs {
196    fn deref_mut(&mut self) -> &mut Vec<NestedMeta> {
197        &mut self.0
198    }
199}
200
201pub(crate) struct Attributes(Vec<syn::Attribute>);
202
203impl Parse for Attributes {
204    fn parse(input: ParseStream) -> syn::Result<Self> {
205        Ok(Self(syn::Attribute::parse_outer(input)?))
206    }
207}
208
209#[inline]
210pub(crate) fn is_attr(attrs: &[syn::Attribute], ident: &str) -> bool {
211    attrs.iter().any(|attr| {
212        attr.path
213            .segments
214            .last()
215            .expect("No segments in path")
216            .ident
217            == ident
218    })
219}
220
221pub(crate) const INK_PREFIX: &str = "ink=";
222
223#[inline]
224pub(crate) fn skip() -> bool {
225    !std::env::args().any(|arg| arg.contains(INK_PREFIX))
226}