use super::*;
struct PartsInput {
base: syn::TypePath,
def: Permission,
fields: IndexMap<syn::Ident, Permission>,
}
struct FieldGroup {
perm: Permission,
fields: Vec<FieldSpec>,
}
struct DefSpec {
perm: Permission,
token: Token![*],
}
enum FieldSpec {
Named(syn::Ident),
Def(Token![*]),
}
use FieldSpec as FS;
impl Parse for PartsInput {
#[throws(syn::parse::Error)]
fn parse(input: ParseStream<'_>) -> Self {
let base = input.parse()?;
let mut def: Option<DefSpec> = None;
let mut fields: IndexMap<_,_> = default();
while ! input.is_empty() {
let g: FieldGroup = input.parse()?;
for f in g.fields {
let already_specified = |here: Span2, there: Span2| {
emit_error!{
here, "access permission respecified";
note = there => "previously specified here";
}
};
match f {
FS::Named(i) => {
use indexmap::map::Entry::*;
match fields.entry(i.clone()) {
Occupied(o) => already_specified(i.span(), o.key().span()),
Vacant(v) => { v.insert(g.perm); },
}
},
FS::Def(token) => {
match &def {
Some(y) => already_specified(token.span, y.token.span),
None => def = Some(DefSpec { perm: g.perm, token }),
}
}
}
}
}
PartsInput {
base,
fields,
def: def.as_ref().map(|ds| ds.perm).unwrap_or(Permission::Const),
}
}
}
impl Parse for FieldGroup {
#[throws(syn::parse::Error)]
fn parse(input: ParseStream<'_>) -> Self {
let perm = input.parse()?;
let mut fields = vec![];
while ! input.is_empty() {
let la = input.lookahead1();
let field =
if la.peek(Token![,]) {
let _: Token![,] = input.parse()?;
break;
} else if la.peek(syn::Ident) {
FS::Named(input.parse()?)
} else if la.peek(Token![*]) {
FS::Def(input.parse()?)
} else {
throw!(la.error())
};
fields.push(field);
}
FieldGroup { perm, fields }
}
}
impl Parse for Permission {
#[throws(syn::parse::Error)]
fn parse(input: ParseStream<'_>) -> Self {
let la = input.lookahead1();
if la.peek(Token![mut]) {
let _: Token![mut] = input.parse()?;
Permission::Mut
} else if la.peek(Token![const]) {
let _: Token![const] = input.parse()?;
Permission::Const
} else if la.peek(Token![!]) {
let _: Token![!] = input.parse()?;
Permission::No
} else {
throw!(la.error())
}
}
}
#[proc_macro_error(allow_not_macro)]
pub fn partial(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let dhow = DebugVar::new();
let dhow = dhow.mode();
if DebugMode::wants(&dhow,1) {
eprintln!("PARTIAL IN partial!( {} )", &input);
}
let us = ourselves_ident();
let input: PartsInput = parse_macro_input!(input);
let base = &input.base;
let mut output = {
let base_span = (||{
let last = base.path.segments.last()?;
Some(last.ident.span())
})();
let def_ty = match base_span {
Some(s) => format_ident!("All_{}", input.def, span=s),
None => format_ident!("All_{}", input.def),
};
quote!{ <#base as #us::PartialBorrow>::#def_ty }
};
for (field,perm) in input.fields {
let perm = format_ident!{"{}", perm};
output = quote!{
<
#output
as
#us::perms::Adjust <
#us::perms::#perm,
{ <#base as #us::PartialBorrow>::FIELDS.#field },
>
> ::Adjusted
};
}
if DebugMode::wants(&dhow,1) {
let output = output.to_string();
let output = output.split_ascii_whitespace().join(" ");
let output = output.replace(" :: ","::");
eprintln!("PARTIAL OUT {}", &output);
}
output.into()
}