office2pdf 0.5.0

Convert DOCX, XLSX, and PPTX files to PDF using pure Rust
Documentation
use super::*;
use std::fs;
use std::time::{SystemTime, UNIX_EPOCH};

struct TempDir {
    path: PathBuf,
}

impl TempDir {
    fn new(prefix: &str) -> Self {
        let unique = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("system time should be valid")
            .as_nanos();
        let path = std::env::temp_dir().join(format!("{prefix}-{unique}"));
        fs::create_dir_all(&path).unwrap();
        Self { path }
    }

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

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

#[test]
fn test_discover_macos_office_font_paths_prefers_office_order() {
    let temp = TempDir::new("office-font-discovery-order");
    let apps = temp.path().join("Applications");
    let home = temp.path().join("home");

    fs::create_dir_all(apps.join("Microsoft PowerPoint.app/Contents/Resources/DFonts")).unwrap();
    fs::create_dir_all(apps.join("Microsoft Word.app/Contents/Resources/DFonts")).unwrap();
    fs::create_dir_all(
        home.join("Library/Group Containers/UBF8T346G9.Office/FontCache/4/CloudFonts"),
    )
    .unwrap();
    fs::create_dir_all(
        home.join("Library/Group Containers/UBF8T346G9.Office/FontCache/4/PreviewFont"),
    )
    .unwrap();

    let discovered = discover_macos_office_font_paths_from(&[apps], &home);
    let expected = vec![
        fs::canonicalize(
            temp.path()
                .join("Applications/Microsoft PowerPoint.app/Contents/Resources/DFonts"),
        )
        .unwrap(),
        fs::canonicalize(
            temp.path()
                .join("Applications/Microsoft Word.app/Contents/Resources/DFonts"),
        )
        .unwrap(),
        fs::canonicalize(
            temp.path()
                .join("home/Library/Group Containers/UBF8T346G9.Office/FontCache/4/CloudFonts"),
        )
        .unwrap(),
        fs::canonicalize(
            temp.path()
                .join("home/Library/Group Containers/UBF8T346G9.Office/FontCache/4/PreviewFont"),
        )
        .unwrap(),
    ];

    assert_eq!(discovered, expected);
}

#[test]
fn test_discover_macos_office_font_paths_selects_highest_font_cache_version() {
    let temp = TempDir::new("office-font-discovery-version");
    let apps = temp.path().join("Applications");
    let home = temp.path().join("home");

    fs::create_dir_all(
        home.join("Library/Group Containers/UBF8T346G9.Office/FontCache/4/CloudFonts"),
    )
    .unwrap();
    fs::create_dir_all(
        home.join("Library/Group Containers/UBF8T346G9.Office/FontCache/7/CloudFonts"),
    )
    .unwrap();
    fs::create_dir_all(
        home.join("Library/Group Containers/UBF8T346G9.Office/FontCache/7/PreviewFont"),
    )
    .unwrap();

    let discovered = discover_macos_office_font_paths_from(&[apps], &home);
    assert!(
        discovered
            .iter()
            .any(|path| path.ends_with("FontCache/7/CloudFonts")),
        "highest font cache version should be used"
    );
    assert!(
        discovered
            .iter()
            .all(|path| !path.ends_with("FontCache/4/CloudFonts")),
        "older font cache versions should be ignored"
    );
}

#[test]
fn test_merge_prioritized_paths_keeps_first_occurrence() {
    let temp = TempDir::new("office-font-merge");
    let first = temp.path().join("first");
    let second = temp.path().join("second");
    fs::create_dir_all(&first).unwrap();
    fs::create_dir_all(&second).unwrap();

    let merged = merge_prioritized_paths(
        &canonicalize_existing_dirs(vec![first.clone(), second.clone()]),
        &canonicalize_existing_dirs(vec![second, first]),
    );

    assert_eq!(merged.len(), 2);
    assert!(merged[0].ends_with("first"));
    assert!(merged[1].ends_with("second"));
}

#[test]
fn test_canonicalize_existing_dirs_skips_missing_paths() {
    let temp = TempDir::new("office-font-canonicalize");
    let existing = temp.path().join("existing");
    fs::create_dir_all(&existing).unwrap();
    let missing = temp.path().join("missing");

    let canonicalized =
        canonicalize_existing_dirs(vec![existing.clone(), missing, existing.clone()]);

    assert_eq!(canonicalized.len(), 1);
    assert_eq!(canonicalized[0], fs::canonicalize(existing).unwrap());
}