automorph 0.2.0

Derive macros for bidirectional Automerge-Rust struct synchronization
Documentation
//! Implementations for path types.
//!
//! This module provides implementations for:
//! - `PathBuf` - Owned path
//! - `OsString` - Owned OS string

use std::ffi::OsString;
use std::path::PathBuf;

use automerge::{ChangeHash, ObjId, Prop, ReadDoc, transaction::Transactable};

use crate::{Automorph, Error, PrimitiveChanged, Result, ScalarCursor};

// PathBuf stored as a string (UTF-8)
// Note: This will fail for non-UTF-8 paths on some platforms
impl Automorph for PathBuf {
    type Changes = PrimitiveChanged;
    type Cursor = ScalarCursor;

    fn save<D: Transactable + ReadDoc>(
        &self,
        doc: &mut D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
    ) -> Result<()> {
        let s = self
            .to_str()
            .ok_or_else(|| Error::invalid_value("path is not valid UTF-8"))?;
        s.to_string().save(doc, obj, prop)
    }

    fn load<D: ReadDoc>(doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>) -> Result<Self> {
        let s = String::load(doc, obj, prop)?;
        Ok(PathBuf::from(s))
    }

    fn load_at<D: ReadDoc>(
        doc: &D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
        heads: &[ChangeHash],
    ) -> Result<Self> {
        let s = String::load_at(doc, obj, prop, heads)?;
        Ok(PathBuf::from(s))
    }

    fn diff<D: ReadDoc>(
        &self,
        doc: &D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
    ) -> Result<Self::Changes> {
        let doc_value = Self::load(doc, obj, prop)?;
        Ok(PrimitiveChanged::new(*self != doc_value))
    }

    fn diff_at<D: ReadDoc>(
        &self,
        doc: &D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
        heads: &[ChangeHash],
    ) -> Result<Self::Changes> {
        let doc_value = Self::load_at(doc, obj, prop, heads)?;
        Ok(PrimitiveChanged::new(*self != doc_value))
    }

    fn update<D: ReadDoc>(
        &mut self,
        doc: &D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
    ) -> Result<Self::Changes> {
        let new_value = Self::load(doc, obj, prop)?;
        let changed = *self != new_value;
        if changed {
            *self = new_value;
        }
        Ok(PrimitiveChanged::new(changed))
    }

    fn update_at<D: ReadDoc>(
        &mut self,
        doc: &D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
        heads: &[ChangeHash],
    ) -> Result<Self::Changes> {
        let new_value = Self::load_at(doc, obj, prop, heads)?;
        let changed = *self != new_value;
        if changed {
            *self = new_value;
        }
        Ok(PrimitiveChanged::new(changed))
    }
}

// OsString stored as a string (UTF-8)
// Note: This will fail for non-UTF-8 strings on some platforms
impl Automorph for OsString {
    type Changes = PrimitiveChanged;
    type Cursor = ScalarCursor;

    fn save<D: Transactable + ReadDoc>(
        &self,
        doc: &mut D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
    ) -> Result<()> {
        let s = self
            .to_str()
            .ok_or_else(|| Error::invalid_value("OsString is not valid UTF-8"))?;
        s.to_string().save(doc, obj, prop)
    }

    fn load<D: ReadDoc>(doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>) -> Result<Self> {
        let s = String::load(doc, obj, prop)?;
        Ok(OsString::from(s))
    }

    fn load_at<D: ReadDoc>(
        doc: &D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
        heads: &[ChangeHash],
    ) -> Result<Self> {
        let s = String::load_at(doc, obj, prop, heads)?;
        Ok(OsString::from(s))
    }

    fn diff<D: ReadDoc>(
        &self,
        doc: &D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
    ) -> Result<Self::Changes> {
        let doc_value = Self::load(doc, obj, prop)?;
        Ok(PrimitiveChanged::new(*self != doc_value))
    }

    fn diff_at<D: ReadDoc>(
        &self,
        doc: &D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
        heads: &[ChangeHash],
    ) -> Result<Self::Changes> {
        let doc_value = Self::load_at(doc, obj, prop, heads)?;
        Ok(PrimitiveChanged::new(*self != doc_value))
    }

    fn update<D: ReadDoc>(
        &mut self,
        doc: &D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
    ) -> Result<Self::Changes> {
        let new_value = Self::load(doc, obj, prop)?;
        let changed = *self != new_value;
        if changed {
            *self = new_value;
        }
        Ok(PrimitiveChanged::new(changed))
    }

    fn update_at<D: ReadDoc>(
        &mut self,
        doc: &D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
        heads: &[ChangeHash],
    ) -> Result<Self::Changes> {
        let new_value = Self::load_at(doc, obj, prop, heads)?;
        let changed = *self != new_value;
        if changed {
            *self = new_value;
        }
        Ok(PrimitiveChanged::new(changed))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use automerge::{AutoCommit, ROOT};

    #[test]
    fn test_pathbuf() {
        let mut doc = AutoCommit::new();

        let path = PathBuf::from("/home/user/file.txt");
        path.save(&mut doc, &ROOT, "path").unwrap();

        let restored = PathBuf::load(&doc, &ROOT, "path").unwrap();
        assert_eq!(restored, PathBuf::from("/home/user/file.txt"));
    }

    #[test]
    fn test_osstring() {
        let mut doc = AutoCommit::new();

        let s = OsString::from("hello.txt");
        s.save(&mut doc, &ROOT, "name").unwrap();

        let restored = OsString::load(&doc, &ROOT, "name").unwrap();
        assert_eq!(restored, OsString::from("hello.txt"));
    }
}