1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use {
crate::{
core::{
cache::{AsCacheRef, CacheRead, CacheWrite, DeserCache},
primitive::CommitLog,
storage,
workspace::{self, AsWorkspaceRef, Guard, Status, Workspace},
Bytes, Map,
},
Addr, Error, Path,
},
tokio::io,
};
pub struct Fixity<C, W> {
storage: C,
workspace: W,
}
impl<C, W> Fixity<C, W> {
pub fn new(storage: C, workspace: W) -> Self {
Self { storage, workspace }
}
pub fn into_cache(self) -> C {
self.into_cw().0
}
pub fn into_cw(self) -> (C, W) {
(self.storage, self.workspace)
}
}
impl Fixity<DeserCache<()>, workspace::Memory> {
pub fn memory() -> Self {
Self {
storage: DeserCache::new(()),
workspace: workspace::Memory::new("default".to_owned()),
}
}
}
impl<C, W> Fixity<C, W>
where
C: CacheRead + CacheWrite,
{
pub fn map(&self, path: Path) -> Map<'_, C, W> {
Map::new(&self.storage, &self.workspace, path)
}
pub fn bytes(&self, path: Path) -> Result<Bytes<'_, C, W>, Error> {
if path.is_empty() {
return Err(Error::CannotReplaceRootMap);
}
if !path.is_root_map() {
return Err(Error::CannotReplaceRootMap);
}
Ok(Bytes::new(&self.storage, &self.workspace, path))
}
}
#[derive(Debug, thiserror::Error)]
pub enum InitError {
#[error("failed creating fixity directory: `{source}`")]
CreateDir { source: io::Error },
#[error("failed creating new storage: `{source}`")]
Storage {
#[from]
source: storage::Error,
},
}
impl<C, W> AsWorkspaceRef for Fixity<C, W>
where
W: Workspace,
{
type Workspace = W;
fn as_workspace_ref(&self) -> &Self::Workspace {
&self.workspace
}
}
impl<C, W> AsCacheRef for Fixity<C, W>
where
C: CacheRead + CacheWrite,
{
type Cache = C;
fn as_cache_ref(&self) -> &Self::Cache {
&self.storage
}
}
#[async_trait::async_trait]
pub trait Commit {
async fn commit(&self) -> Result<Addr, Error>;
}
#[async_trait::async_trait]
impl<T> Commit for T
where
T: AsWorkspaceRef + AsCacheRef + Sync,
{
async fn commit(&self) -> Result<Addr, Error> {
let storage = self.as_cache_ref();
let workspace = self.as_workspace_ref();
let workspace_guard = workspace.lock().await?;
let (commit_addr, staged_content) = match workspace_guard.status().await? {
Status::InitStaged { staged_content, .. } => (None, staged_content),
Status::Staged {
commit,
staged_content,
..
} => (Some(commit), staged_content),
Status::Detached(_) => return Err(Error::DetachedHead),
Status::Init { .. } | Status::Clean { .. } => {
return Err(Error::NoStageToCommit);
},
};
let mut commit_log = CommitLog::new(storage, commit_addr);
let commit_addr = commit_log.append(staged_content).await?;
workspace_guard.commit(commit_addr.clone()).await?;
Ok(commit_addr)
}
}