kcode-rust-bins 2.0.0

Author, locally publish, and run small Rust binaries
Documentation
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};

use kcode_rust_bins::{File, Lib, create, docs, open};

static TEST_COUNTER: AtomicU64 = AtomicU64::new(0);

struct TestRoot(PathBuf);

impl TestRoot {
    fn new(label: &str) -> Self {
        let counter = TEST_COUNTER.fetch_add(1, Ordering::Relaxed);
        let path = std::env::temp_dir().join(format!(
            "kcode-rust-bins-integration-{label}-{}-{counter}",
            std::process::id()
        ));
        fs::create_dir(&path).unwrap();
        Self(path)
    }

    fn path(&self) -> &Path {
        &self.0
    }
}

impl Drop for TestRoot {
    fn drop(&mut self) {
        let _ = fs::remove_dir_all(&self.0);
    }
}

fn contents_mut<'a>(library: &'a mut Lib, path: &str) -> &'a mut String {
    &mut library
        .files
        .iter_mut()
        .find(|file| file.path == path)
        .unwrap()
        .contents
}

#[test]
fn create_open_and_docs_return_source_once_without_a_lockfile() {
    let root = TestRoot::new("open");
    let publications = root.path().join("published");
    let created = create(root.path(), &publications, "demo").unwrap();
    assert_eq!(
        created
            .files
            .iter()
            .map(|file| file.path.as_str())
            .collect::<Vec<_>>(),
        ["Cargo.toml", "Documentation.md", "src/main.rs"]
    );
    assert_eq!(
        created
            .files
            .iter()
            .filter(|file| file.path == "Documentation.md")
            .count(),
        1
    );
    assert!(created.files.iter().all(|file| file.path != "Cargo.lock"));

    let opened = open(root.path(), &publications, "demo").unwrap();
    assert_eq!(opened.files, created.files);
    assert_eq!(
        docs(root.path(), "demo").unwrap(),
        ("0.1.0".to_owned(), String::new())
    );
}

#[test]
fn complete_replacement_removes_omissions_and_refreshes_identity() {
    let root = TestRoot::new("replace");
    let publications = root.path().join("published");
    let mut library = create(root.path(), &publications, "demo").unwrap();
    library.files.push(File {
        path: "tests/temporary.rs".to_owned(),
        contents: "#[test]\nfn temporary() {}\n".to_owned(),
    });
    library.write().unwrap();
    assert!(
        open(root.path(), &publications, "demo")
            .unwrap()
            .files
            .iter()
            .any(|file| file.path == "tests/temporary.rs")
    );

    library
        .files
        .retain(|file| file.path != "tests/temporary.rs");
    *contents_mut(&mut library, "Documentation.md") = "second write\n".to_owned();
    library.write().unwrap();
    let reopened = open(root.path(), &publications, "demo").unwrap();
    assert!(
        reopened
            .files
            .iter()
            .all(|file| file.path != "tests/temporary.rs")
    );
    assert_eq!(docs(root.path(), "demo").unwrap().1, "second write\n");
}

#[test]
fn stale_writers_cannot_both_commit() {
    let root = TestRoot::new("stale");
    let publications = root.path().join("published");
    create(root.path(), &publications, "demo").unwrap();
    let mut first = open(root.path(), &publications, "demo").unwrap();
    let mut second = open(root.path(), &publications, "demo").unwrap();
    *contents_mut(&mut first, "Documentation.md") = "first\n".to_owned();
    *contents_mut(&mut second, "Documentation.md") = "second\n".to_owned();
    first.write().unwrap();
    let error = second.write().unwrap_err();
    assert!(error.to_string().starts_with("stale_snapshot:"));
    assert_eq!(docs(root.path(), "demo").unwrap().1, "first\n");
}

#[test]
fn open_and_docs_migrate_a_legacy_flat_binary() {
    let root = TestRoot::new("migration");
    let repository = root.path().join("legacy");
    fs::create_dir(&repository).unwrap();
    fs::create_dir(repository.join("src")).unwrap();
    fs::write(
        repository.join("Cargo.toml"),
        "[package]\nname = \"legacy\"\nversion = \"2.3.4\"\nedition = \"2024\"\n",
    )
    .unwrap();
    fs::write(repository.join("Documentation.md"), "legacy API\n").unwrap();
    fs::write(repository.join("src/main.rs"), "fn main() {}\n").unwrap();
    fs::write(repository.join("Cargo.lock"), "version = 4\n").unwrap();

    let opened = open(root.path(), root.path().join("published"), "legacy").unwrap();
    assert!(opened.files.iter().all(|file| file.path != "Cargo.lock"));
    assert_eq!(
        docs(root.path(), "legacy").unwrap(),
        ("2.3.4".to_owned(), "legacy API\n".to_owned())
    );
    assert!(repository.join("HEAD").is_file());
    assert!(repository.join(".lock").is_file());
    assert!(repository.join("generations").is_dir());
}