protokit_textformat 0.2.0

Usable protocol buffers
Documentation
#![allow(clippy::wrong_self_convention, clippy::new_ret_no_self)]

use std::collections::BTreeMap;
use std::fmt::{Debug, Formatter};
use std::ops::Deref;

use binformat::{BinProto, InputStream, OutputStream, SizeStack};

use crate::TextProto;

#[derive(Default, Debug)]
pub struct Registry {
    pub messages: BTreeMap<&'static str, Box<dyn AnyMessage>>,
}

impl Registry {
    pub fn init(init: fn(&mut Self)) -> Self {
        let mut out = Self::default();
        init(&mut out);
        out
    }

    pub fn register(&mut self, msg: &dyn AnyMessage) {
        self.messages.insert(msg.qualified_name(), msg.new());
    }

    pub fn find(&self, name: &str) -> Option<&dyn AnyMessage> {
        self.messages.get(name).map(|v| v.deref())
    }
}

impl Clone for Box<dyn AnyMessage> {
    fn clone(&self) -> Self {
        self.new()
    }
}

/// type-erased message that supports all formats of decoding & encoding
pub trait AnyMessage: Send + Sync + 'static {
    fn new(&self) -> Box<dyn AnyMessage + 'static>;
    fn qualified_name(&self) -> &'static str;

    fn as_bin<'buf>(&self) -> &dyn binformat::BinProto<'buf>;
    fn as_bin_mut<'buf>(&mut self) -> &mut dyn binformat::BinProto<'buf>;

    fn as_text<'buf>(&self) -> &dyn crate::TextProto<'buf>;
    fn as_text_mut<'buf>(&mut self) -> &mut dyn crate::TextProto<'buf>;
}

impl Debug for dyn AnyMessage {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        f.debug_tuple("AnyMessage").field(&self.qualified_name()).finish()
    }
}

impl<T> AnyMessage for T
where
    T: for<'buf> BinProto<'buf> + for<'buf> TextProto<'buf>,
    T: Default + Debug + Send + Sync + 'static,
{
    fn new(&self) -> Box<dyn AnyMessage + 'static> {
        Box::<T>::default()
    }

    fn qualified_name(&self) -> &'static str {
        <T as BinProto>::qualified_name(self)
    }

    fn as_bin<'buf>(&self) -> &dyn BinProto<'buf> {
        self
    }

    fn as_bin_mut<'buf>(&mut self) -> &mut dyn BinProto<'buf> {
        self
    }

    fn as_text<'buf>(&self) -> &dyn TextProto<'buf> {
        self
    }

    fn as_text_mut<'buf>(&mut self) -> &mut dyn TextProto<'buf> {
        self
    }
}

impl<'buf> BinProto<'buf> for dyn AnyMessage {
    fn qualified_name(&self) -> &'static str {
        self.as_bin().qualified_name()
    }

    fn merge_field(&mut self, tag_wire: u32, stream: &mut InputStream<'buf>) -> binformat::Result<()> {
        self.as_bin_mut().merge_field(tag_wire, stream)
    }

    fn size(&self, stack: &mut SizeStack) -> usize {
        self.as_bin().size(stack)
    }

    fn encode(&self, stream: &mut OutputStream) {
        self.as_bin().encode(stream)
    }
}

impl<'buf> TextProto<'buf> for dyn AnyMessage {
    fn merge_field(&mut self, stream: &mut crate::InputStream<'buf>) -> crate::Result<()> {
        self.as_text_mut().merge_field(stream)
    }

    fn encode(&self, stream: &mut crate::OutputStream) {
        self.as_text().encode(stream)
    }
}