mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-20 03:18:06 +08:00
Restructure project directories (#333)
`/client/web` -> `/frontend` `/client/cli` -> *delete for now* `/client/native` -> *delete for now* `/core/editor` -> `/editor` `/core/document` -> `/graphene` `/core/renderer` -> `/charcoal` `/core/proc-macro` -> `/proc-macros` *(now plural)*
This commit is contained in:
207
proc-macros/src/helper_structs.rs
Normal file
207
proc-macros/src/helper_structs.rs
Normal file
@@ -0,0 +1,207 @@
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use std::collections::HashMap;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::token::Paren;
|
||||
use syn::{parenthesized, LitStr, Token};
|
||||
|
||||
pub struct IdentList {
|
||||
pub parts: Punctuated<Ident, Token![,]>,
|
||||
}
|
||||
|
||||
impl Parse for IdentList {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let content;
|
||||
let _paren_token = parenthesized!(content in input);
|
||||
Ok(Self {
|
||||
parts: Punctuated::parse_terminated(&content)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses `(left, right)`
|
||||
pub struct Pair<F, S> {
|
||||
pub paren_token: Paren,
|
||||
pub first: F,
|
||||
pub sep: Token![,],
|
||||
pub second: S,
|
||||
}
|
||||
|
||||
impl<F, S> Parse for Pair<F, S>
|
||||
where
|
||||
F: Parse,
|
||||
S: Parse,
|
||||
{
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let content;
|
||||
let paren_token = parenthesized!(content in input);
|
||||
Ok(Self {
|
||||
paren_token,
|
||||
first: content.parse()?,
|
||||
sep: content.parse()?,
|
||||
second: content.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// parses `(...)`
|
||||
pub struct ParenthesizedTokens {
|
||||
pub paren: Paren,
|
||||
pub tokens: TokenStream,
|
||||
}
|
||||
|
||||
impl Parse for ParenthesizedTokens {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let content;
|
||||
let paren = parenthesized!(content in input);
|
||||
Ok(Self { paren, tokens: content.parse()? })
|
||||
}
|
||||
}
|
||||
|
||||
/// parses a comma-delimeted list of `T`s with optional trailing comma
|
||||
pub struct SimpleCommaDelimeted<T>(pub Vec<T>);
|
||||
|
||||
impl<T: Parse> Parse for SimpleCommaDelimeted<T> {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let punct = Punctuated::<T, Token![,]>::parse_terminated(input)?;
|
||||
Ok(Self(punct.into_iter().collect()))
|
||||
}
|
||||
}
|
||||
|
||||
#[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