1use proc_macro2::TokenStream;
2use quote::{quote, quote_spanned};
3use syn;
4
5struct ModeInput {
6 user: syn::Ident,
7 group: syn::Ident,
8 other: syn::Ident
9}
10impl ModeInput {
11 fn mode(&self, compile_errors: &mut Vec<TokenStream>) -> u32 {
12 Self::parse_ident(&self.user, "user", 6, 2, 'S', compile_errors) |
13 Self::parse_ident(&self.group, "group", 3, 1, 'S', compile_errors) |
14 Self::parse_ident(&self.other, "other", 0, 0, 'T', compile_errors)
15 }
16 fn parse_ident(mode: &syn::Ident, domain: &str, shift: u32, top_shift: u32, top_bit: char, compile_errors: &mut Vec<TokenStream>) -> u32 {
17 let mut err = |str: &str| compile_errors.push(quote_spanned!{mode.span()=> ::core::compile_error!(#str)});
18 let mode = mode.to_string();
19 let mut bits = mode.chars();
20 let mut match_bit = |b: char, e: char| if b == e {
21 1
22 } else if b == '_' {
23 0
24 } else {
25 err(&format!("invalid mode bit {b}"));
26 0
27 };
28 match (bits.next(), bits.next(), bits.next(), bits.next(), bits.next()) {
29 (Some(r), Some(w), Some(x), top, None) => {
30 ((
31 (match_bit(r, 'r') << 2) |
32 (match_bit(w, 'w') << 1) |
33 match_bit(x, 'x')
34 ) << shift) |
35 (match_bit(top.unwrap_or('_'), top_bit) << (top_shift + 9))
36 },
37 (_, _, _, _, Some(_)) => {
38 err(&format!("extra {domain} mode bits"));
39 0
40 }
41 _ => {
42 err(&format!("missing {domain} mode bits"));
43 0
44 }
45 }
46 }
47}
48impl syn::parse::Parse for ModeInput {
49 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
50 Ok(Self {
51 user: input.parse()?,
52 group: input.parse()?,
53 other: input.parse()?
54 })
55 }
56}
57
58#[proc_macro]
59pub fn mode(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
60 let mode = syn::parse_macro_input!(input as ModeInput);
61 let mut compile_errors = Vec::new();
62 let mode = mode.mode(&mut compile_errors);
63 let compile_errors = compile_errors.iter();
64 quote!{
65 ::syslib::open::Mode(#mode) #(| #compile_errors)*
66 }.into()
67}