cueward-adapter-macos 0.5.0

macOS adapter for Cueward with Safari, Notes, Messages, Reminders, Calendar, Screenshot, Clipboard, and OCR integrations.
use super::*;
use cueward_core::files::relocation::RelocationPlatform;
use cueward_core::files::{DataState, FileStamp, ResourceMetadata, ResourceValue};
use std::cell::Cell;
use std::fs::{File, Metadata};
use std::io;

struct Inspecting {
    calls: Cell<usize>,
    change: Box<dyn Fn()>,
    alias: Option<bool>,
    available: bool,
    same_device: bool,
}
impl FilePlatform for Inspecting {
    fn stamp(&self, metadata: &Metadata) -> FileStamp {
        let mut stamp = MacFiles.stamp(metadata);
        if !self.available && metadata.is_file() {
            stamp.data_state = DataState::Unknown;
        }
        stamp
    }
    fn open_regular(&self, path: &Path) -> io::Result<File> {
        MacFiles.open_regular(path)
    }
    fn resource_metadata(
        &self,
        path: &Path,
        metadata: &Metadata,
    ) -> Result<ResourceMetadata, FileError> {
        let calls = self.calls.get() + 1;
        self.calls.set(calls);
        if calls == 2 {
            (self.change)();
        }
        let mut resources = MacFiles.resource_metadata(path, metadata)?;
        resources.is_alias_file = self
            .alias
            .map(|value| ResourceValue::Available { value })
            .unwrap_or(ResourceValue::Unavailable);
        Ok(resources)
    }
}
impl RelocationPlatform for Inspecting {
    fn open_directory(&self, path: &Path) -> Result<File, FileError> {
        RelocationPlatform::open_directory(&MacFiles, path)
    }
    fn same_filesystem(&self, _: &Metadata, _: &Metadata) -> bool {
        self.same_device
    }
    fn rename(
        &self,
        _: &File,
        _: &cueward_core::files::relocation::RelocationRequest,
    ) -> cueward_core::files::relocation::RenameOutcome {
        panic!("read-only batch planning must not rename");
    }
}
impl BatchRenamePlatform for Inspecting {
    fn names_may_collide(&self, a: &str, b: &str) -> bool {
        MacFiles.names_may_collide(a, b)
    }
}
fn inspecting(change: impl Fn() + 'static) -> Inspecting {
    Inspecting {
        calls: Cell::new(0),
        change: Box::new(change),
        alias: Some(false),
        available: true,
        same_device: true,
    }
}
#[test]
fn batch_rename_rechecks_earlier_sources_after_later_proposals_are_observed() {
    let root = fixture();
    let request = request(root.path(), &[("a", "new"), ("b", "other")]);
    let path = root.path().join("a");
    let platform = inspecting(move || {
        fs::write(&path, b"OWNED externally changed a").unwrap();
    });
    assert_eq!(
        batch::plan(&platform, &request).unwrap_err().code,
        FileErrorCode::Changed
    );
    assert!(!root.path().join("new").exists());
    assert_eq!(
        fs::read(root.path().join("a")).unwrap(),
        b"OWNED externally changed a"
    );
}
#[test]
fn batch_rename_rechecks_destination_conflicts_that_change_between_items() {
    let root = fixture();
    fs::write(
        root.path().join("nested/existing"),
        b"OWNED old destination",
    )
    .unwrap();
    let request = request(root.path(), &[("nested/c", "existing"), ("b", "other")]);
    let path = root.path().join("nested/existing");
    let platform = inspecting(move || {
        fs::write(&path, b"OWNED new destination").unwrap();
    });
    assert_eq!(
        batch::plan(&platform, &request).unwrap_err().code,
        FileErrorCode::Changed
    );
    assert_eq!(
        fs::read(root.path().join("nested/existing")).unwrap(),
        b"OWNED new destination"
    );
    assert!(root.path().join("nested/c").exists());
}
#[test]
fn batch_rename_alias_and_availability_failures_are_explicit_item_errors() {
    let root = fixture();
    let request = request(root.path(), &[("a", "new")]);
    for (alias, available, code) in [
        (None, true, FileErrorCode::Unavailable),
        (Some(false), false, FileErrorCode::Unavailable),
    ] {
        let mut platform = inspecting(|| {});
        platform.alias = alias;
        platform.available = available;
        let plan = batch::plan(&platform, &request).unwrap();
        assert!(plan.has_conflicts);
        assert_eq!(plan.items[0].error.as_ref().unwrap().code, code);
        assert!(plan.items[0].proposal.is_none());
    }
    let mut platform = inspecting(|| {});
    platform.alias = Some(true);
    let plan = batch::plan(&platform, &request).unwrap();
    assert!(!plan.has_conflicts && plan.items[0].error.is_none());
    assert!(root.path().join("a").exists());
    assert!(!root.path().join("new").exists());
}
#[test]
fn batch_rename_different_device_observation_is_not_a_copy_fallback() {
    let root = fixture();
    let request = request(root.path(), &[("a", "new")]);
    let mut platform = inspecting(|| {});
    platform.same_device = false;
    let plan = batch::plan(&platform, &request).unwrap();
    assert!(plan.has_conflicts && has(&plan, BatchRenameIssueCode::DifferentFilesystem, &[0]));
    assert!(root.path().join("a").exists());
    assert!(!root.path().join("new").exists());
}