Skip to main content

redox_core/
io.rs

1//! Core IO helpers for loading/saving text buffers.
2//!
3//! This module is intentionally small and UI-agnostic. It just provides helpers
4//! that read and write UTF-8 text to/from the rope-backed `TextBuffer`.
5
6use std::path::Path;
7
8use anyhow::{Context as _, Result};
9
10use crate::buffer::TextBuffer;
11
12/// Read a UTF-8 file into a `TextBuffer`.
13///
14/// This is a simple, whole-file read (simple for the early development stage):
15/// - loads entire file into memory
16/// - requires valid UTF-8
17///
18/// Might add higher-level functions for encoding detection and streaming IO later
19pub fn load_buffer(path: impl AsRef<Path>) -> Result<TextBuffer> {
20    let path = path.as_ref();
21
22    let bytes = std::fs::read(path)
23        .with_context(|| format!("failed to read file: {}", path.to_string_lossy()))?;
24
25    let text = String::from_utf8(bytes)
26        .with_context(|| format!("file is not valid UTF-8: {}", path.to_string_lossy()))?;
27
28    Ok(TextBuffer::from_str(&text))
29}
30
31/// Write a `TextBuffer` to a UTF-8 file.
32///
33/// This writes the entire buffer to disk in one go.
34/// Will add variants later for stuff like incremental or atomic writes.
35pub fn save_buffer(path: impl AsRef<Path>, buffer: &TextBuffer) -> Result<()> {
36    let path = path.as_ref();
37    std::fs::write(path, buffer.to_string())
38        .with_context(|| format!("failed to write file: {}", path.to_string_lossy()))?;
39    Ok(())
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45    use std::time::{SystemTime, UNIX_EPOCH};
46
47    fn temp_path(tag: &str) -> std::path::PathBuf {
48        let nanos = SystemTime::now()
49            .duration_since(UNIX_EPOCH)
50            .expect("clock went backwards")
51            .as_nanos();
52        std::env::temp_dir().join(format!("redox_io_test_{tag}_{nanos}.txt"))
53    }
54
55    #[test]
56    fn save_then_load_roundtrip() {
57        let path = temp_path("roundtrip");
58        let b = TextBuffer::from_str("hello\nworld\n");
59
60        save_buffer(&path, &b).expect("save failed");
61        let loaded = load_buffer(&path).expect("load failed");
62
63        assert_eq!(loaded.to_string(), "hello\nworld\n");
64        let _ = std::fs::remove_file(path);
65    }
66
67    #[test]
68    fn load_buffer_rejects_invalid_utf8() {
69        let path = temp_path("invalid_utf8");
70        std::fs::write(&path, [0xffu8, 0xfeu8]).expect("failed to write invalid utf8");
71
72        let err = load_buffer(&path).expect_err("expected invalid utf8 error");
73        let msg = err.to_string();
74        assert!(msg.contains("not valid UTF-8") || msg.contains("file is not valid UTF-8"));
75
76        let _ = std::fs::remove_file(path);
77    }
78}