rullst-core 12.3.0

Core runtime, routing engine, and telemetry for the Rullst framework suite.
Documentation
use serde::Serialize;
use std::io::Write;

pub(super) enum BoundedJsonError {
    LimitExceeded,
    Encode(serde_json::Error),
}

pub(super) fn encode_bounded<T>(value: &T, maximum: usize) -> Result<Vec<u8>, BoundedJsonError>
where
    T: Serialize,
{
    let mut writer = BoundedJsonWriter::new(maximum);
    match serde_json::to_writer(&mut writer, value) {
        Ok(()) => Ok(writer.into_bytes()),
        Err(_) if writer.exceeded() => Err(BoundedJsonError::LimitExceeded),
        Err(error) => Err(BoundedJsonError::Encode(error)),
    }
}

pub(super) fn validate_bounded<T>(value: &T, maximum: usize) -> Result<(), BoundedJsonError>
where
    T: Serialize,
{
    let mut writer = JsonSizeWriter::new(maximum);
    match serde_json::to_writer(&mut writer, value) {
        Ok(()) => Ok(()),
        Err(_) if writer.exceeded() => Err(BoundedJsonError::LimitExceeded),
        Err(error) => Err(BoundedJsonError::Encode(error)),
    }
}

struct BoundedJsonWriter {
    bytes: Vec<u8>,
    maximum: usize,
    exceeded: bool,
}

impl BoundedJsonWriter {
    fn new(maximum: usize) -> Self {
        Self {
            bytes: Vec::new(),
            maximum,
            exceeded: false,
        }
    }

    const fn exceeded(&self) -> bool {
        self.exceeded
    }

    fn into_bytes(self) -> Vec<u8> {
        self.bytes
    }
}

impl Write for BoundedJsonWriter {
    fn write(&mut self, buffer: &[u8]) -> std::io::Result<usize> {
        let remaining = self.maximum.saturating_sub(self.bytes.len());
        if buffer.len() > remaining {
            self.exceeded = true;
            return Err(std::io::Error::other("offline JSON limit reached"));
        }
        self.bytes.extend_from_slice(buffer);
        Ok(buffer.len())
    }

    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

struct JsonSizeWriter {
    written: usize,
    maximum: usize,
    exceeded: bool,
}

impl JsonSizeWriter {
    const fn new(maximum: usize) -> Self {
        Self {
            written: 0,
            maximum,
            exceeded: false,
        }
    }

    const fn exceeded(&self) -> bool {
        self.exceeded
    }
}

impl Write for JsonSizeWriter {
    fn write(&mut self, buffer: &[u8]) -> std::io::Result<usize> {
        let Some(written) = self.written.checked_add(buffer.len()) else {
            self.exceeded = true;
            return Err(std::io::Error::other("offline JSON limit reached"));
        };
        if written > self.maximum {
            self.exceeded = true;
            return Err(std::io::Error::other("offline JSON limit reached"));
        }
        self.written = written;
        Ok(buffer.len())
    }

    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}