affine_core 0.0.2

AFFiNE primitive core.
Documentation
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use thiserror::Error;

#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(tag = "type", rename_all = "camelCase", rename_all_fields = "camelCase")]
pub enum SourceIdentity {
  CurrentDoc {
    workspace_id: String,
    doc_id: String,
  },
  History {
    workspace_id: String,
    doc_id: String,
    timestamp_ms: i64,
  },
}

#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
pub(crate) enum SourceIdentityError {
  #[error("workspace and document ids must not be empty")]
  EmptyId,
  #[error("history timestamp is invalid")]
  InvalidTimestamp,
}

impl SourceIdentity {
  pub(crate) fn validate(&self) -> Result<(), SourceIdentityError> {
    if self.workspace_id().is_empty() || self.doc_id().is_empty() {
      return Err(SourceIdentityError::EmptyId);
    }
    if let Self::History { timestamp_ms, .. } = self
      && DateTime::<Utc>::from_timestamp_millis(*timestamp_ms).is_none()
    {
      return Err(SourceIdentityError::InvalidTimestamp);
    }
    Ok(())
  }

  pub fn workspace_id(&self) -> &str {
    match self {
      Self::CurrentDoc { workspace_id, .. } | Self::History { workspace_id, .. } => workspace_id,
    }
  }

  pub fn doc_id(&self) -> &str {
    match self {
      Self::CurrentDoc { doc_id, .. } | Self::History { doc_id, .. } => doc_id,
    }
  }

  pub fn is_workspace_root(&self) -> bool {
    matches!(
      self,
      Self::CurrentDoc {
        workspace_id,
        doc_id
      } if workspace_id == doc_id
    )
  }
}

#[cfg(test)]
#[path = "tests/blob_access/tests.rs"]
mod tests;