//! F4 — I/O monomers: MC_32–MC_39
//!
//! File and stream I/O. All operations return Result — no panics, no silent failures.

use std::fs;
use std::io::{self, Write};

/// MC_32 — READ: Read all bytes from a file path.
///
/// Returns `Err` if the file cannot be read.
pub fn read_file(path: &str) -> io::Result<Vec<u8>> {
    fs::read(path)
}

/// MC_33 — WRITE: Write bytes to a file path (creates or truncates).
///
/// Returns `Err` if the file cannot be written.
pub fn write_file(path: &str, data: &[u8]) -> io::Result<()> {
    fs::write(path, data)
}

/// MC_34 — APPEND: Append bytes to a file.
pub fn append_file(path: &str, data: &[u8]) -> io::Result<()> {
    use fs::OpenOptions;
    let mut f = OpenOptions::new().append(true).create(true).open(path)?;
    f.write_all(data)
}

/// MC_35 — READ_STR: Read a file as UTF-8 string.
///
/// Returns `Err` if the file cannot be read or is not valid UTF-8.
pub fn read_str(path: &str) -> io::Result<String> {
    fs::read_to_string(path)
}

/// MC_36 — WRITE_STR: Write a UTF-8 string to stdout.
pub fn write_stdout(s: &str) -> io::Result<()> {
    io::stdout().lock().write_all(s.as_bytes())
}

/// MC_37 — WRITE_STDERR: Write a UTF-8 string to stderr.
pub fn write_stderr(s: &str) -> io::Result<()> {
    io::stderr().lock().write_all(s.as_bytes())
}

/// MC_38 — FILE_EXISTS: Check if a path exists.
pub fn file_exists(path: &str) -> bool {
    std::path::Path::new(path).exists()
}

/// MC_39 — FILE_SIZE: Get file size in bytes. Returns `None` if unavailable.
pub fn file_size(path: &str) -> Option<u64> {
    fs::metadata(path).ok().map(|m| m.len())
}
