prns-core 0.3.4

Pure Reticulum engine and wire contract for Personal Reticulum
use super::{RnxCodecError, RnxField};
use crate::rnx::{
    ExecutionConclusion, ExecutionRequestRef, ExecutionResultRef, MAX_COMMAND_BYTES,
    MAX_RETURNED_STREAM_BYTES, MAX_STDIN_BYTES,
};

pub(super) fn validate_timestamp(timestamp: f64, field: RnxField) -> Result<(), RnxCodecError> {
    if timestamp.is_finite() && timestamp >= 0.0 {
        Ok(())
    } else {
        Err(RnxCodecError::InvalidField(field))
    }
}

pub(super) fn validate_request_ref(request: ExecutionRequestRef<'_>) -> Result<(), RnxCodecError> {
    if request.command.len() > MAX_COMMAND_BYTES {
        return Err(RnxCodecError::InvalidField(RnxField::Command));
    }
    if request
        .stdin
        .is_some_and(|stdin| stdin.len() > MAX_STDIN_BYTES)
    {
        return Err(RnxCodecError::InvalidField(RnxField::Stdin));
    }
    if request
        .timeout_seconds
        .is_some_and(|timeout| !timeout.is_finite() || timeout < 0.0)
    {
        return Err(RnxCodecError::InvalidField(RnxField::Timeout));
    }
    Ok(())
}

pub(super) fn validate_result_ref(result: ExecutionResultRef<'_>) -> Result<(), RnxCodecError> {
    let executed = match result {
        ExecutionResultRef::NotExecuted { started_at } => {
            return validate_timestamp(started_at, RnxField::Started);
        }
        ExecutionResultRef::Executed(executed) => executed,
    };
    validate_timestamp(executed.started_at, RnxField::Started)?;
    if executed.stdout.len() > MAX_RETURNED_STREAM_BYTES
        || executed.stderr.len() > MAX_RETURNED_STREAM_BYTES
        || executed.total_stdout < executed.stdout.len() as u64
        || executed.total_stderr < executed.stderr.len() as u64
    {
        return Err(RnxCodecError::IncoherentResult);
    }
    if let ExecutionConclusion::CompletedAt(concluded_at) = executed.conclusion {
        validate_timestamp(concluded_at, RnxField::Concluded)?;
        if concluded_at < executed.started_at {
            return Err(RnxCodecError::IncoherentResult);
        }
    }
    Ok(())
}