tincan-cli 0.1.0

Capture important decisions, their reasoning, and learnings from agent coding sessions in plain Markdown
use std::path::Path;
use std::time::{SystemTime, UNIX_EPOCH};

pub fn unix_timestamp() -> Result<u64, String> {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map(|duration| duration.as_secs())
        .map_err(|error| error.to_string())
}

pub fn slug(value: &str) -> String {
    let mut output = String::new();
    let mut previous_dash = false;
    for character in value.chars().flat_map(char::to_lowercase) {
        if character.is_ascii_alphanumeric() {
            output.push(character);
            previous_dash = false;
        } else if !previous_dash && !output.is_empty() {
            output.push('-');
            previous_dash = true;
        }
    }
    output.trim_matches('-').chars().take(64).collect()
}

pub fn yaml_string(value: &str) -> String {
    format!(
        "\"{}\"",
        value
            .replace('\\', "\\\\")
            .replace('"', "\\\"")
            .replace('\n', "\\n")
    )
}

pub fn display_path(path: &Path) -> String {
    path.to_string_lossy().replace('\\', "/")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn creates_stable_slug() {
        assert_eq!(
            slug("Sequential Gallery Paging!"),
            "sequential-gallery-paging"
        );
    }

    #[test]
    fn escapes_yaml_strings() {
        assert_eq!(yaml_string("a \"quote\""), "\"a \\\"quote\\\"\"");
    }
}