protospec-build 0.3.0

One binary format language to rule them all, One binary format language to find them, One binary format language to bring them all and in the darkness bind them.
Documentation
use crate::{emit_type_ref, PartialType, PartialScalarType};

use super::*;

pub struct VarInt {
    scalar_type: ScalarType,
    unsigned: Type,
}

impl VarInt {
    pub fn new(scalar_type: ScalarType) -> Self {
        VarInt {
            scalar_type,
            unsigned: Type::Scalar(match scalar_type {
                x if !x.is_signed() => x,
                ScalarType::I8 => ScalarType::U8,
                ScalarType::I16 => ScalarType::U16,
                ScalarType::I32 => ScalarType::U32,
                ScalarType::I64 => ScalarType::U64,
                ScalarType::I128 => ScalarType::U128,
                _ => unimplemented!(),
            }),
        }
    }
}

impl ForeignType for VarInt {
    fn assignable_from(&self, type_: &Type) -> bool {
        Type::Scalar(self.scalar_type).assignable_from(type_)
    }

    fn assignable_from_partial(&self, type_: &PartialType) -> bool {
        match type_ {
            PartialType::Type(t) => self.assignable_from(t),
            PartialType::Scalar(PartialScalarType::Some(scalar)) |
            PartialType::Scalar(PartialScalarType::Defaults(scalar))
                => self.assignable_from(&Type::Scalar(*scalar)),
            _ => false,
        }
    }

    fn assignable_to_partial(&self, type_: &PartialType) -> bool {
        match type_ {
            PartialType::Type(t) => self.assignable_to(t),
            PartialType::Any => true,
            PartialType::Scalar(PartialScalarType::Some(scalar)) => self.assignable_from(&Type::Scalar(*scalar)),
            PartialType::Scalar(_) => true,
            _ => false,
        }
    }

    fn assignable_to(&self, type_: &Type) -> bool {
        type_.assignable_from(&Type::Scalar(self.scalar_type))
    }

    fn type_ref(&self) -> TokenStream {
        emit_type_ref(&Type::Scalar(self.scalar_type))
    }

    fn decoding_gen(
        &self,
        source: TokenStream,
        output_ref: TokenStream,
        _arguments: Vec<TokenStream>,
        is_async: bool,
    ) -> TokenStream {
        let inner = self.type_ref();
        let end = (self.scalar_type.size() as f64 / 7.0).ceil() as usize;
        let inner_unsigned = emit_type_ref(&self.unsigned);
        let async_ = map_async(is_async);
        quote! {
            let #output_ref = {
                let mut i = 0usize;
                let mut buf = [0xffu8; 1];
                let mut output: #inner = 0;
                while (buf[0] & 128) == 128 {
                    #source.read_exact(&mut buf[..])#async_?;
                    output |= ((buf[0] as #inner_unsigned & 127) << (i * 7)) as #inner;
                    i += 1;
                    if i > #end {
                        break;
                    }
                }
                output
            };
        }
    }

    fn encoding_gen(
        &self,
        target: TokenStream,
        field_ref: TokenStream,
        _arguments: Vec<TokenStream>,
        is_async: bool,
    ) -> TokenStream {
        let inner_unsigned = emit_type_ref(&self.unsigned);
        let async_ = map_async(is_async);
        quote! {
            {
                let mut value = #field_ref.clone() as #inner_unsigned;
                while (value & !0b1111111) != 0 {
                    #target.write_all(&[(value as u8 & 127) | 128])#async_?;
                    value >>= 7;
                }
                #target.write_all(&[value as u8])#async_?;
            }
        }
    }

    fn arguments(&self) -> Vec<TypeArgument> {
        vec![]
    }

    fn can_receive_auto(&self) -> Option<ScalarType> {
        Some(self.scalar_type)
    }
}