syslib_macro/
lib.rs

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}