use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt::{Debug, Error, Formatter};
use thiserror::Error;
use crate::backend::{CommitId, Timestamp};
use crate::content_hash::ContentHash;
content_hash! {
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct WorkspaceId(String);
}
impl Debug for WorkspaceId {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
f.debug_tuple("WorkspaceId").field(&self.0).finish()
}
}
impl Default for WorkspaceId {
fn default() -> Self {
Self("default".to_string())
}
}
impl WorkspaceId {
pub fn new(value: String) -> Self {
Self(value)
}
pub fn as_str(&self) -> &str {
&self.0
}
}
content_hash! {
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct ViewId(Vec<u8>);
}
impl Debug for ViewId {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
f.debug_tuple("ViewId").field(&self.hex()).finish()
}
}
impl ViewId {
pub fn new(value: Vec<u8>) -> Self {
Self(value)
}
pub fn from_hex(hex: &str) -> Self {
Self(hex::decode(hex).unwrap())
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn to_bytes(&self) -> Vec<u8> {
self.0.clone()
}
pub fn hex(&self) -> String {
hex::encode(&self.0)
}
}
content_hash! {
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct OperationId(Vec<u8>);
}
impl Debug for OperationId {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
f.debug_tuple("OperationId").field(&self.hex()).finish()
}
}
impl OperationId {
pub fn new(value: Vec<u8>) -> Self {
Self(value)
}
pub fn from_hex(hex: &str) -> Self {
Self(hex::decode(hex).unwrap())
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn to_bytes(&self) -> Vec<u8> {
self.0.clone()
}
pub fn hex(&self) -> String {
hex::encode(&self.0)
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum RefTarget {
Normal(CommitId),
Conflict {
removes: Vec<CommitId>,
adds: Vec<CommitId>,
},
}
impl ContentHash for RefTarget {
fn hash(&self, state: &mut impl digest::Update) {
use RefTarget::*;
match *self {
Normal(ref id) => {
state.update(&0u32.to_le_bytes());
id.hash(state);
}
Conflict {
ref removes,
ref adds,
} => {
state.update(&1u32.to_le_bytes());
removes.hash(state);
adds.hash(state);
}
}
}
}
impl RefTarget {
pub fn is_conflict(&self) -> bool {
matches!(self, RefTarget::Conflict { .. })
}
pub fn adds(&self) -> Vec<CommitId> {
match self {
RefTarget::Normal(id) => {
vec![id.clone()]
}
RefTarget::Conflict { removes: _, adds } => adds.clone(),
}
}
pub fn has_add(&self, needle: &CommitId) -> bool {
match self {
RefTarget::Normal(id) => id == needle,
RefTarget::Conflict { removes: _, adds } => adds.contains(needle),
}
}
pub fn removes(&self) -> Vec<CommitId> {
match self {
RefTarget::Normal(_) => {
vec![]
}
RefTarget::Conflict { removes, adds: _ } => removes.clone(),
}
}
}
content_hash! {
#[derive(Default, PartialEq, Eq, Clone, Debug)]
pub struct BranchTarget {
pub local_target: Option<RefTarget>,
pub remote_targets: BTreeMap<String, RefTarget>,
}
}
content_hash! {
#[derive(PartialEq, Eq, Clone, Debug, Default)]
pub struct View {
pub head_ids: HashSet<CommitId>,
pub public_head_ids: HashSet<CommitId>,
pub branches: BTreeMap<String, BranchTarget>,
pub tags: BTreeMap<String, RefTarget>,
pub git_refs: BTreeMap<String, RefTarget>,
pub git_head: Option<CommitId>,
pub wc_commit_ids: HashMap<WorkspaceId, CommitId>,
}
}
content_hash! {
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Operation {
pub view_id: ViewId,
pub parents: Vec<OperationId>,
pub metadata: OperationMetadata,
}
}
content_hash! {
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct OperationMetadata {
pub start_time: Timestamp,
pub end_time: Timestamp,
pub description: String,
pub hostname: String,
pub username: String,
pub tags: HashMap<String, String>,
}
}
#[derive(Debug, Error)]
pub enum OpStoreError {
#[error("Operation not found")]
NotFound,
#[error("{0}")]
Other(String),
}
pub type OpStoreResult<T> = Result<T, OpStoreError>;
pub trait OpStore: Send + Sync + Debug {
fn read_view(&self, id: &ViewId) -> OpStoreResult<View>;
fn write_view(&self, contents: &View) -> OpStoreResult<ViewId>;
fn read_operation(&self, id: &OperationId) -> OpStoreResult<Operation>;
fn write_operation(&self, contents: &Operation) -> OpStoreResult<OperationId>;
}