use std::path::Path;
use agplatform::Env;
use agplatform::FileType;
use agplatform::Fs;
use agplatform::Platform;
fn save(fs: &impl Fs, dir: &Path, name: &str, content: &str) -> agplatform::Result<()> {
fs.create_dir_all(dir)?;
let tmp = dir.join(format!(".{name}.tmp"));
fs.write(&tmp, content)?;
fs.rename(&tmp, dir.join(format!("{name}.md")))
}
fn load_all(fs: &impl Fs, dir: &Path) -> agplatform::Result<Vec<(String, String)>> {
let mut notes = Vec::new();
for entry in fs.read_dir(dir)? {
let entry = entry?;
if entry.file_type() == FileType::File
&& let Some(name) = entry.file_name().strip_suffix(".md")
{
notes.push((name.to_string(), fs.read_to_string(entry.path())?));
}
}
notes.sort();
Ok(notes)
}
fn main() -> agplatform::Result<()> {
let platform = agplatform::platform();
let dir = platform.env().tmp_dir().join("agplatform_example_notes");
save(platform.fs(), &dir, "hello", "Hello, notes!")?;
for (name, content) in load_all(platform.fs(), &dir)? {
println!("{name}: {content}");
}
platform.fs().remove_dir_all(dir)
}
#[cfg(test)]
mod tests {
use super::*;
use agplatform::TestFs;
use agplatform::test_platform::test_fs::FailMode;
mod save {
use super::*;
#[test]
fn save_should_create_missing_dir() {
let fs = TestFs::new();
save(&fs, Path::new("notes/2026"), "todo", "write examples").unwrap();
assert_eq!(
fs.read_to_string("notes/2026/todo.md").unwrap(),
"write examples"
);
}
#[test]
fn save_should_replace_previous_content() {
let fs = TestFs::new();
save(&fs, Path::new("notes"), "todo", "old").unwrap();
save(&fs, Path::new("notes"), "todo", "new").unwrap();
assert_eq!(fs.read_to_string("notes/todo.md").unwrap(), "new");
assert_eq!(fs.read_dir("notes").unwrap().count(), 1);
}
#[test]
fn save_failing_write_should_keep_previous_content() {
let mut fs = TestFs::new();
save(&fs, Path::new("notes"), "todo", "old").unwrap();
fs.set_fail_mode(Some(FailMode::Write));
let result = save(&fs, Path::new("notes"), "todo", "new");
assert_eq!(result.unwrap_err().kind(), agplatform::ErrorKind::Fs);
fs.set_fail_mode(None);
assert_eq!(fs.read_to_string("notes/todo.md").unwrap(), "old");
}
}
mod load_all {
use super::*;
#[test]
fn load_all_should_return_notes_sorted_by_name() {
let fs = TestFs::new();
save(&fs, Path::new("notes"), "b", "second").unwrap();
save(&fs, Path::new("notes"), "a", "first").unwrap();
let notes = load_all(&fs, Path::new("notes")).unwrap();
assert_eq!(
notes,
[
("a".to_string(), "first".to_string()),
("b".to_string(), "second".to_string())
]
);
}
#[test]
fn load_all_should_skip_other_files_and_dirs() {
let fs = TestFs::new();
fs.create_dir_all("notes/archive.md").unwrap();
fs.write("notes/image.png", "").unwrap();
fs.write("notes/note.md", "text").unwrap();
let notes = load_all(&fs, Path::new("notes")).unwrap();
assert_eq!(notes, [("note".to_string(), "text".to_string())]);
}
#[test]
fn load_all_missing_dir_should_fail() {
let fs = TestFs::new();
let error = load_all(&fs, Path::new("notes")).unwrap_err();
assert_eq!(error.kind(), agplatform::ErrorKind::Fs);
}
}
}