// Copyright 2021 Ian Jackson and contributors // SPDX-License-Identifier: GPL-3.0-or-later // There is NO WARRANTY. use super::*; struct PartsInput { base: syn::TypePath, def: Permission, fields: IndexMap, } struct FieldGroup { perm: Permission, fields: Vec, } 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 = 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() }