partial-borrow-macros 1.0.1

proc-macros for partial-borrow
Documentation
// 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<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()
}