mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 02:48:12 +08:00
Add proc macros for Hint and edge (#63)
* Add proc-macro crate with two macros * Let cargo recalculate the Cargo.lock * Add tests and refactor some code to allow testing also the impl for parse_hint_helper_attrs now preserves order (which is essential for testing)
This commit is contained in:
144
core/proc-macro/src/structs.rs
Normal file
144
core/proc-macro/src/structs.rs
Normal file
@@ -0,0 +1,144 @@
|
||||
use proc_macro2::Ident;
|
||||
use std::collections::HashMap;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::token::Paren;
|
||||
use syn::{parenthesized, LitStr, Token};
|
||||
|
||||
/// Parses `("some text")`
|
||||
pub struct AttrInnerSingleString {
|
||||
_paren_token: Paren,
|
||||
pub content: LitStr,
|
||||
}
|
||||
|
||||
impl Parse for AttrInnerSingleString {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let content;
|
||||
let _paren_token = parenthesized!(content in input);
|
||||
Ok(Self {
|
||||
_paren_token,
|
||||
content: content.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses `key="value"`
|
||||
pub struct KeyEqString {
|
||||
key: Ident,
|
||||
_eq_token: Token![=],
|
||||
lit: LitStr,
|
||||
}
|
||||
|
||||
impl Parse for KeyEqString {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
Ok(Self {
|
||||
key: input.parse()?,
|
||||
_eq_token: input.parse()?,
|
||||
lit: input.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses `(key="value", key="value", …)`
|
||||
pub struct AttrInnerKeyStringMap {
|
||||
_paren_token: Paren,
|
||||
parts: Punctuated<KeyEqString, Token![,]>,
|
||||
}
|
||||
|
||||
impl Parse for AttrInnerKeyStringMap {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let content;
|
||||
let _paren_token = parenthesized!(content in input);
|
||||
Ok(Self {
|
||||
_paren_token,
|
||||
parts: Punctuated::parse_terminated(&content)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AttrInnerKeyStringMap {
|
||||
pub fn multi_into_iter(iter: impl IntoIterator<Item = Self>) -> impl Iterator<Item = (Ident, Vec<LitStr>)> {
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
let mut res = Vec::<(Ident, Vec<LitStr>)>::new();
|
||||
let mut idx = HashMap::<Ident, usize>::new();
|
||||
|
||||
for part in iter.into_iter().flat_map(|x: Self| x.parts) {
|
||||
match idx.entry(part.key) {
|
||||
Entry::Occupied(occ) => {
|
||||
res[*occ.get()].1.push(part.lit);
|
||||
}
|
||||
Entry::Vacant(vac) => {
|
||||
let ident = vac.key().clone();
|
||||
vac.insert(res.len());
|
||||
res.push((ident, vec![part.lit]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn attr_inner_single_string() {
|
||||
let res = syn::parse2::<AttrInnerSingleString>(quote::quote! {
|
||||
("a string literal")
|
||||
});
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(res.ok().unwrap().content.value(), "a string literal");
|
||||
|
||||
let res = syn::parse2::<AttrInnerSingleString>(quote::quote! {
|
||||
wrong, "stuff"
|
||||
});
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_eq_string() {
|
||||
let res = syn::parse2::<KeyEqString>(quote::quote! {
|
||||
key="value"
|
||||
});
|
||||
assert!(res.is_ok());
|
||||
let res = res.ok().unwrap();
|
||||
assert_eq!(res.key, "key");
|
||||
assert_eq!(res.lit.value(), "value");
|
||||
|
||||
let res = syn::parse2::<KeyEqString>(quote::quote! {
|
||||
wrong, "stuff"
|
||||
});
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attr_inner_key_string_map() {
|
||||
let res = syn::parse2::<AttrInnerKeyStringMap>(quote::quote! {
|
||||
(key="value", key2="value2")
|
||||
});
|
||||
assert!(res.is_ok());
|
||||
let res = res.ok().unwrap();
|
||||
for (item, (k, v)) in res.parts.into_iter().zip(vec![("key", "value"), ("key2", "value2")]) {
|
||||
assert_eq!(item.key, k);
|
||||
assert_eq!(item.lit.value(), v);
|
||||
}
|
||||
|
||||
let res = syn::parse2::<AttrInnerKeyStringMap>(quote::quote! {
|
||||
(key="value", key2="value2",)
|
||||
});
|
||||
assert!(res.is_ok());
|
||||
let res = res.ok().unwrap();
|
||||
for (item, (k, v)) in res.parts.into_iter().zip(vec![("key", "value"), ("key2", "value2")]) {
|
||||
assert_eq!(item.key, k);
|
||||
assert_eq!(item.lit.value(), v);
|
||||
}
|
||||
|
||||
let res = syn::parse2::<AttrInnerKeyStringMap>(quote::quote! {
|
||||
wrong, "stuff"
|
||||
});
|
||||
assert!(res.is_err());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user