protokit_derive 0.1.1

Usable protocol buffers
Documentation
use core::fmt::{Display, Formatter};

use proc_macro2::{Ident, Span};
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::{bracketed, parenthesized, Error, Lifetime, LitInt, LitStr, Token};

pub const VARINT: u8 = 0;
pub const FIX64: u8 = 1;
pub const BYTES: u8 = 2;
pub const SGRP: u8 = 3;
pub const _EGRP: u8 = 4;
pub const FIX32: u8 = 5;

#[derive(Default)]
pub struct ProtoMeta {
    pub buf: Option<Lifetime>,
    pub name: Option<LitStr>,
    pub file: Option<LitStr>,
    pub package: Option<LitStr>,
}

impl Parse for ProtoMeta {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let mut out = Self::default();
        let mut oname: Option<Ident> = input.parse()?;
        for _ in 0 .. 100 {
            let Some(name) = &oname else {
                return Ok(out);
            };

            let _: Token![=] = input.parse()?;
            if name == "buf" || name == "borrow" {
                out.buf = Some(input.parse()?);
            } else if name == "name" {
                out.name = Some(input.parse()?);
            } else if name == "package" {
                out.package = Some(input.parse()?);
            }

            let _: Option<Token![,]> = input.parse()?;

            oname = input.parse()?;
        }
        Err(Error::new(input.span(), "Could not recognize proto meta"))
    }
}

pub struct VarMeta {
    pub num: LitInt,
    pub name: LitStr,
}

impl Parse for VarMeta {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let num: LitInt = input.parse()?;
        let _: Token![,] = input.parse()?;
        let name: LitStr = input.parse()?;

        Ok(Self { num, name })
    }
}

pub struct FieldMeta {
    pub num: LitInt,
    pub name: Option<LitStr>,
    pub kind: FieldKind,
    pub freq: Frequency,
}

fn err(s: Span, m: impl Display, e: syn::Error) -> syn::Error {
    syn::Error::new(s, format!("{} {:?}", m, e))
}

impl Parse for FieldMeta {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let num: LitInt = input.parse().map_err(|e| err(e.span(), "Expected field number", e))?;
        let _: Token![,] = input.parse()?;
        let name = if input.peek(LitStr) {
            let out = input.parse().map_err(|e| err(e.span(), "Expected field name", e))?;
            let _: Token![,] = input.parse()?;
            Some(out)
        } else {
            None
        };
        let kind: FieldKind = input.parse().map_err(|e| err(e.span(), "Expected field kind", e))?;
        let freq = if input.peek(Token![,]) {
            let _: Token![,] = input.parse()?;
            input.parse()?
        } else {
            Frequency::Singular
        };

        Ok(Self { num, name, kind, freq })
    }
}

#[derive(Debug, PartialEq)]
pub enum FieldKind {
    Varint,
    Sigint,
    Bool,
    ProtoEnum,
    Fixed32,
    Fixed64,
    Bytes,
    String,
    Nested,
    Group,
    Map(Box<(FieldKind, FieldKind)>),
}

impl FieldKind {
    pub fn is_scalar(&self) -> bool {
        matches!(
            self,
            Self::Varint | Self::Sigint | Self::ProtoEnum | Self::Bool | Self::Fixed32 | Self::Fixed64
        )
    }
    pub fn wire_type(&self) -> u8 {
        match self {
            FieldKind::Varint | FieldKind::Sigint | FieldKind::ProtoEnum | FieldKind::Bool => VARINT,
            FieldKind::Fixed32 => FIX32,
            FieldKind::Fixed64 => FIX64,
            FieldKind::Bytes | FieldKind::String | FieldKind::Nested | FieldKind::Map(_) => BYTES,
            FieldKind::Group => SGRP,
        }
    }
}

impl Display for FieldKind {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        match self {
            FieldKind::Varint => write!(f, "varint"),
            FieldKind::Sigint => write!(f, "sigint"),
            FieldKind::ProtoEnum => write!(f, "protoenum"),
            FieldKind::Bool => write!(f, "bool"),
            FieldKind::Fixed32 => write!(f, "fixed32"),
            FieldKind::Fixed64 => write!(f, "fixed64"),
            FieldKind::Bytes => write!(f, "bytes"),
            FieldKind::String => write!(f, "string"),
            FieldKind::Nested => write!(f, "nested"),
            FieldKind::Group => write!(f, "group"),
            FieldKind::Map(k) => write!(f, "map({},{})", &k.0, &k.1),
        }
    }
}

impl Parse for FieldKind {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let ident: Ident = input.parse()?;
        Ok(if ident == "varint" {
            FieldKind::Varint
        } else if ident == "sigint" {
            FieldKind::Sigint
        } else if ident == "protoenum" {
            FieldKind::ProtoEnum
        } else if ident == "bool" {
            FieldKind::Bool
        } else if ident == "fixed32" {
            FieldKind::Fixed32
        } else if ident == "fixed64" {
            FieldKind::Fixed64
        } else if ident == "bytes" {
            FieldKind::Bytes
        } else if ident == "string" {
            FieldKind::String
        } else if ident == "nested" {
            FieldKind::Nested
        } else if ident == "group" {
            FieldKind::Group
        } else if ident == "map" {
            let params;
            let _ = parenthesized!(params in input);
            let k: FieldKind = params.parse()?;
            let _: Token![,] = params.parse()?;
            let v: FieldKind = params.parse()?;
            FieldKind::Map(Box::new((k, v)))
        } else {
            return Err(Error::new(
                ident.span(),
                format!(
                    "Unknown field kind: {}, expected varint, fixed, nested, or group",
                    ident
                ),
            ));
        })
    }
}

#[derive(Debug, Default, Eq, PartialEq)]
pub enum Frequency {
    #[default]
    Raw,
    Singular,
    Optional,
    Repeated,
    Required,
    Packed,
}

impl Frequency {
    pub fn is_multi(&self) -> bool {
        matches!(self, Self::Repeated | Self::Packed)
    }
    pub fn binformat_suffix(&self) -> &'static str {
        match self {
            Frequency::Raw => "raw",
            Frequency::Singular | Frequency::Required => "single",
            Frequency::Packed => "packed",
            Frequency::Repeated => "repeated",
            Frequency::Optional => "optional",
        }
    }
    pub fn textformat_suffix(&self) -> &'static str {
        match self {
            Frequency::Raw => "raw",
            Frequency::Singular | Frequency::Required => "single",
            Frequency::Repeated | Frequency::Packed => "repeated",
            Frequency::Optional => "optional",
        }
    }
}

impl Parse for Frequency {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let ident: Ident = input.parse()?;
        Ok(if ident == "raw" {
            Frequency::Raw
        } else if ident == "singular" {
            Frequency::Singular
        } else if ident == "optional" {
            Frequency::Optional
        } else if ident == "repeated" {
            Frequency::Repeated
        } else if ident == "required" {
            Frequency::Required
        } else if ident == "packed" {
            Frequency::Packed
        } else {
            return Err(syn::Error::new(
                ident.span(),
                format!(
                    "Unknown field frequency: {}, expected singular, optional or repeated",
                    ident
                ),
            ));
        })
    }
}

impl Display for Frequency {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        match self {
            Frequency::Raw => write!(f, "raw"),
            Frequency::Singular => write!(f, "singular"),
            Frequency::Optional => write!(f, "optional"),
            Frequency::Repeated => write!(f, "repeated"),
            Frequency::Required => write!(f, "singular"),
            Frequency::Packed => write!(f, "packed"),
        }
    }
}

pub struct OneOfMeta {
    pub nums: Vec<LitInt>,
    pub names: Vec<LitStr>,
}

impl Parse for OneOfMeta {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let nums;
        let _ = bracketed!(nums in input);
        let nums = Punctuated::<LitInt, Token![,]>::parse_terminated(&nums)?;
        let _: Token![,] = input.parse()?;
        let names;
        let _ = bracketed!(names in input);
        let names = Punctuated::<LitStr, Token![,]>::parse_terminated(&names)?;

        Ok(Self {
            nums: nums.into_iter().collect(),
            names: names.into_iter().collect(),
        })
    }
}