syd 3.52.0

rock-solid application kernel
Documentation
//
// Syd: rock-solid application kernel
// src/parsers/mod.rs: Syd's nom parsers
//
// Copyright (c) 2024, 2025, 2026 Ali Polatel <alip@chesswob.org>
// This file is based in part upon procinfo-rs crate which is:
//   Copyright (c) 2015 The Rust Project Developers
//   SPDX-License-Identifier: MIT
//
// SPDX-License-Identifier: GPL-3.0

// SAFETY: This module has been liberated from unsafe code!
#![forbid(unsafe_code)]

pub(crate) mod proc;
pub mod sandbox;

use std::os::fd::AsFd;

use nix::{errno::Errno, unistd::read};
use nom::{Finish, IResult};

/// Read all bytes in the file until EOF, placing them into `buf`.
///
/// All bytes read from this source will be written to `buf`.  If `buf` is not large enough an
/// underflow error will be returned. This function will continuously call `read` to append more
/// data to `buf` until read returns either `Ok(0)`, or an error of non-`ErrorKind::Interrupted`
/// kind.
///
/// If successful, this function will return the slice of read bytes.
///
/// # Errors
///
/// If this function encounters an error of the kind `ErrorKind::Interrupted` then the error is
/// ignored and the operation will continue.
///
/// If any other read error is encountered then this function immediately returns.  Any bytes which
/// have already been read will be written to `buf`.
///
/// If `buf` is not large enough to hold the file, an underflow error will be returned.
pub(crate) fn read_to_end<Fd: AsFd>(fd: Fd, buf: &mut [u8]) -> Result<&mut [u8], Errno> {
    let mut from = 0;

    loop {
        if from == buf.len() {
            return Err(Errno::EOVERFLOW); // read underflow.
        }
        match read(&fd, &mut buf[from..]) {
            Ok(0) => return Ok(&mut buf[..from]),
            Ok(n) => from = from.checked_add(n).ok_or(Errno::EOVERFLOW)?,
            Err(Errno::EINTR) => {}
            Err(errno) => return Err(errno),
        }
    }
}

/// Transforms a `nom` parse result into a `Result` with `nix::errno::Errno`.
///
/// The parser does not have to completely consume the input.
pub(crate) fn map_result<T>(result: IResult<&[u8], T>) -> nix::Result<T> {
    match result.finish() {
        Ok((_, val)) => Ok(val),
        Err(_) => Err(Errno::EINVAL),
    }
}

#[cfg(test)]
mod tests {
    use nom::bytes::complete::tag;

    use super::*;

    #[test]
    fn test_map_result_1() {
        let result: IResult<&[u8], &[u8]> = Ok((&b""[..], &b"hello"[..]));
        assert_eq!(map_result(result).unwrap(), b"hello");
    }

    #[test]
    fn test_map_result_2() {
        // map_result does not require full consumption.
        let result: IResult<&[u8], &[u8]> = Ok((&b"rest"[..], &b"val"[..]));
        assert_eq!(map_result(result).unwrap(), b"val");
    }

    #[test]
    fn test_map_result_3() {
        let result: IResult<&[u8], &[u8]> = Err(nom::Err::Error(nom::error::Error::new(
            &b""[..],
            nom::error::ErrorKind::Tag,
        )));
        assert_eq!(map_result(result), Err(Errno::EINVAL));
    }

    #[test]
    fn test_map_result_4() {
        let input = b"hello world";
        let result = tag::<&[u8], &[u8], nom::error::Error<&[u8]>>(b"hello")(input);
        let val = map_result(result).unwrap();
        assert_eq!(val, b"hello");
    }

    #[test]
    fn test_map_result_5() {
        let input = b"world";
        let result = tag::<&[u8], &[u8], nom::error::Error<&[u8]>>(b"hello")(input);
        assert_eq!(map_result(result), Err(Errno::EINVAL));
    }
}