posthog_symbol_data/
symbol_data.rs

1use crate::{error::Error, utils::assert_at_least_as_long_as};
2
3const MAGIC: &[u8] = b"posthog_error_tracking";
4const VERSION: u32 = 1;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum SymbolDataType {
8    SourceAndMap = 2,
9    HermesMap = 3,
10    ProguardMapping = 4,
11}
12
13pub trait SymbolData: Sized {
14    fn from_bytes(data: Vec<u8>) -> Result<Self, Error>;
15    fn into_bytes(self) -> Vec<u8>;
16    fn data_type() -> SymbolDataType;
17}
18
19pub fn write<T>(data: T) -> Result<Vec<u8>, Error>
20where
21    T: SymbolData,
22{
23    let d_type = T::data_type();
24    let bytes = data.into_bytes();
25    let mut buffer = Vec::with_capacity(header_len() + bytes.len());
26    buffer.extend_from_slice(MAGIC);
27    buffer.extend_from_slice(&VERSION.to_le_bytes());
28    buffer.extend_from_slice(&(d_type as u32).to_le_bytes());
29    buffer.extend_from_slice(&bytes);
30    Ok(buffer)
31}
32
33pub fn read_as<T>(data: Vec<u8>) -> Result<T, Error>
34where
35    T: SymbolData,
36{
37    assert_at_least_as_long_as(header_len(), data.len())?;
38    assert_has_magic(&data)?;
39    assert_version(&data)?;
40    assert_data_type(&data, T::data_type())?;
41    T::from_bytes(data[header_len()..].to_vec())
42}
43
44pub fn assert_version(buffer: &[u8]) -> Result<(), Error> {
45    let version = u32::from_le_bytes(buffer[MAGIC.len()..MAGIC.len() + 4].try_into().unwrap());
46    if version > VERSION {
47        return Err(Error::WrongVersion(version, VERSION));
48    }
49    Ok(())
50}
51
52fn assert_has_magic(buffer: &[u8]) -> Result<(), Error> {
53    if &buffer[..MAGIC.len()] != MAGIC {
54        return Err(Error::InvalidMagic);
55    }
56    Ok(())
57}
58
59pub fn assert_data_type(buffer: &[u8], expected_type: SymbolDataType) -> Result<(), Error> {
60    let data_type = u32::from_le_bytes(buffer[header_len() - 4..header_len()].try_into().unwrap());
61    if data_type != expected_type as u32 {
62        Err(Error::InvalidDataType(
63            data_type,
64            format!("{expected_type:?}"),
65        ))
66    } else {
67        Ok(())
68    }
69}
70
71const fn header_len() -> usize {
72    // Magic + Version + Type
73    MAGIC.len() + VERSION.to_le_bytes().len() + std::mem::size_of::<u32>()
74}