Add local file conflict metadata

This commit is contained in:
Eric Wendland 2026-05-18 03:57:26 +02:00
commit 20c88af800
13 changed files with 673 additions and 15 deletions

View file

@ -21,6 +21,12 @@ pub enum CasError {
NonRelativeTreePath(String),
#[error("invalid file root name: {0}")]
InvalidFileRootName(String),
#[error("invalid file conflict kind: {0}")]
InvalidFileConflictKind(String),
#[error("invalid file conflict resolution: {0}")]
InvalidFileConflictResolution(String),
#[error("invalid file conflict status: {0}")]
InvalidFileConflictStatus(String),
}
#[derive(Clone, Debug)]
@ -96,6 +102,108 @@ pub enum FileRootChange {
Renamed { from: String, to: String },
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct FileConflict {
pub id: String,
pub root: String,
pub resource: String,
pub path: String,
pub kind: FileConflictKind,
pub status: FileConflictStatus,
pub base_tree: Option<BlobHash>,
pub local_tree: Option<BlobHash>,
pub remote_tree: Option<BlobHash>,
pub detail: String,
pub resolution: Option<FileConflictResolution>,
pub resolution_note: Option<String>,
pub created_at_ms: i64,
pub resolved_at_ms: Option<i64>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum FileConflictKind {
ConcurrentEdit,
DeleteEdit,
Rename,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum FileConflictStatus {
Open,
Resolved,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum FileConflictResolution {
KeepLocal,
AcceptRemote,
KeepBoth,
Manual,
}
impl FileConflictKind {
pub fn parse(value: &str) -> Result<Self, CasError> {
match value {
"concurrent-edit" => Ok(Self::ConcurrentEdit),
"delete-edit" => Ok(Self::DeleteEdit),
"rename" => Ok(Self::Rename),
_ => Err(CasError::InvalidFileConflictKind(value.to_owned())),
}
}
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::ConcurrentEdit => "concurrent-edit",
Self::DeleteEdit => "delete-edit",
Self::Rename => "rename",
}
}
}
impl FileConflictResolution {
pub fn parse(value: &str) -> Result<Self, CasError> {
match value {
"keep-local" => Ok(Self::KeepLocal),
"accept-remote" => Ok(Self::AcceptRemote),
"keep-both" => Ok(Self::KeepBoth),
"manual" => Ok(Self::Manual),
_ => Err(CasError::InvalidFileConflictResolution(value.to_owned())),
}
}
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::KeepLocal => "keep-local",
Self::AcceptRemote => "accept-remote",
Self::KeepBoth => "keep-both",
Self::Manual => "manual",
}
}
}
impl FileConflictStatus {
pub fn parse(value: &str) -> Result<Self, CasError> {
match value {
"open" => Ok(Self::Open),
"resolved" => Ok(Self::Resolved),
_ => Err(CasError::InvalidFileConflictStatus(value.to_owned())),
}
}
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Open => "open",
Self::Resolved => "resolved",
}
}
}
impl LocalCas {
#[must_use]
pub fn new(root: impl Into<PathBuf>) -> Self {