font-test-data 0.6.1

Test data for the fontations crates
Documentation
//! small utilities used in tests

use font_types::Scalar;
use std::collections::HashMap;

/// A convenience type for generating a buffer of big-endian bytes.
#[derive(Debug, Clone, Default)]
pub struct BeBuffer {
    data: Vec<u8>,
    tagged_locations: HashMap<String, usize>,
}

impl BeBuffer {
    pub fn new() -> Self {
        Default::default()
    }

    /// The current length of the buffer in bytes.
    pub fn len(&self) -> usize {
        self.data.len()
    }

    /// Returns `true` if the buffer contains zero bytes.
    pub fn is_empty(&self) -> bool {
        self.data.is_empty()
    }

    /// Return a reference to the contents of the buffer
    pub fn as_slice(&self) -> &[u8] {
        &self.data
    }

    /// Write any scalar to this buffer.
    pub fn push(mut self, item: impl Scalar) -> Self {
        self.data.extend(item.to_raw().as_ref());
        self
    }

    pub fn push_with_tag(mut self, item: impl Scalar, tag: &str) -> Self {
        self.tagged_locations
            .insert(tag.to_string(), self.data.len());
        self.data.extend(item.to_raw().as_ref());
        self
    }

    /// Write multiple scalars into the buffer
    pub fn extend<T: Scalar>(mut self, iter: impl IntoIterator<Item = T>) -> Self {
        for item in iter {
            self.data.extend(item.to_raw().as_ref());
        }
        self
    }

    pub fn offset_for(&self, tag: &str) -> usize {
        // panic on unrecognized tags
        self.tagged_locations.get(tag).copied().unwrap()
    }

    fn data_for(&mut self, tag: &str) -> &mut [u8] {
        let offset = self.offset_for(tag);
        &mut self.data[offset..]
    }

    pub fn write_at(&mut self, tag: &str, item: impl Scalar) {
        let data = self.data_for(tag);
        let raw = item.to_raw();
        let new_data: &[u8] = raw.as_ref();

        if data.len() < new_data.len() {
            panic!("not enough room left in buffer for the requested write.");
        }

        for (left, right) in data.iter_mut().zip(new_data) {
            *left = *right
        }
    }

    pub fn data(&self) -> &[u8] {
        &self.data
    }
}

impl std::ops::Deref for BeBuffer {
    type Target = [u8];
    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

/// be_buffer_add!(buffer, value) - Add an item to a be_buffer.
///
/// Will call the appropriate insertion method on BeBuffer depending on the values format.
///
/// Values can be one of three types:
/// 1. Single numeric value, ie: 10u16
/// 2. Array of numeric values, ie: [1u8, 2, 3]
/// 3. Tagged value: {10u16, "tag_name"}. This will insert the numeric value
///    and attach the provide string tag to the insertion location in the buffer.
#[macro_export]
macro_rules! be_buffer_add {
    ($b:ident, $v:literal) => {
        let $b = $b.push($v);
    };
    ($b:ident, {$v:tt : $tag:literal}) => {
        let $b = $b.push_with_tag($v, $tag);
    };
    ($b:ident, [$($v:literal),+]) => {
        let $b = $b.extend([$($v),*]);
    };
    ($b:ident, $v:tt) => {
        let $b = $b.push($v);
    };
}

/// be_buffer!(val1, ..., valn) - Constructs a BeBuffer from the provided list of values:
///
/// Values can be one of three types:
/// 1. Single numeric value, ie: 10u16
/// 2. Array of numeric values, ie: [1u8, 2, 3]
/// 3. Tagged value: {10u16, "tag_name"}. This will insert the numeric value
///    and attach the provide string tag to the insertion location in the buffer.
#[macro_export]
macro_rules! be_buffer {
    ( $( $x:tt ),+ ) => {
        {
            let builder = BeBuffer::new();
            $(
                $crate::be_buffer_add!(builder, $x);
            )*
            builder
        }
    };
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn be_buffer_macro() {
        let data: &[u8] = &be_buffer! {
            1u8,
            2u16,
            3u32
        };

        assert_eq!([1, 0, 2, 0, 0, 0, 3], data);
    }

    #[test]
    fn be_buffer_macro_array() {
        let data: &[u8] = &be_buffer! {
            1u8,
            [2u8, 3, 4, 5, 6]
        };

        assert_eq!([1, 2, 3, 4, 5, 6], data);
    }

    #[test]
    fn be_buffer_macro_tagged() {
        let builder = be_buffer! {
            1u8,
            {2u16: "foo"},
            {(u32::from(3u16)): "bar"}
        };
        let data: &[u8] = &builder;

        assert_eq!([1, 0, 2, 0, 0, 0, 3], data);
        assert_eq!(builder.offset_for("foo"), 1);
        assert_eq!(builder.offset_for("bar"), 3);
    }
}