1pub mod audit;
2pub mod auth;
3pub mod backends;
4mod cache;
5pub mod config;
6pub mod error;
7mod files;
8pub mod git;
9mod gitignore;
10pub mod globbing;
11mod hashes;
12pub mod init;
13pub mod operation;
14pub mod paths;
15pub mod progress;
16pub(crate) mod utils;
17
18pub use backends::{Backend, RetrieveRequest, StoreRequest, StoreResult};
19pub use config::Compression;
20pub use error::{Error, ErrorCode, backend_error_code};
21pub use files::add::{AddDetail, AddReport, AddResult, add_files};
22pub use files::get::{
23 Action, Before, GetCode, GetCounts, GetOptions, GetOutcome, GetReport, GetRow, GetVerdict,
24 StoredCopy, get_files, shell_path,
25};
26pub use files::metadata::FileMetadata;
27pub use files::status::{Baseline, StatusFacts, StatusReport, WorkingCopy, inspect_status};
28pub use files::types::{Outcome, Status};
29pub use hashes::{HashAlg, Hashes};
30pub use operation::{
31 ByteTotals, CancelToken, Counts, FileRow, OperationContext, OperationObserver, OperationPlan,
32 OperationRow, RowState, RunOutcome, TransferObserver,
33};
34pub use paths::{AddPathStatus, DvsPaths, PathFilter, ProjectPath, find_repo_root};
35pub use progress::FileProgress;
36pub use utils::{format_size, set_num_threads};
37
38pub const VERSION: &str = env!("CARGO_PKG_VERSION");
39
40#[cfg(test)]
41pub mod testutil {
42 use fs_err as fs;
43 use std::path::{Path, PathBuf};
44 use tempfile::TempDir;
45
46 use crate::config::Config;
47 use crate::init::init;
48
49 pub fn create_temp_git_repo() -> (TempDir, PathBuf) {
55 let tmp = tempfile::tempdir().unwrap();
56 let repo_root = fs::canonicalize(tmp.path()).unwrap().join("repo");
60 fs::create_dir(&repo_root).unwrap();
61 fs::create_dir(repo_root.join(".git")).unwrap();
62 (tmp, repo_root)
63 }
64
65 pub fn create_file(dir: &Path, relative_path: &str, content: &[u8]) -> PathBuf {
69 let path = dir.join(relative_path);
70 if let Some(parent) = path.parent() {
71 fs::create_dir_all(parent).ok();
72 }
73 fs::write(&path, content).unwrap();
74 path
75 }
76
77 pub fn init_dvs_repo(repo_root: &Path) -> (Config, PathBuf) {
82 let storage_dir = repo_root.parent().unwrap().join(".storage");
83 let config = Config::new_local(&storage_dir, None).unwrap();
84 init(repo_root, config.clone()).unwrap();
85 let dvs_dir = repo_root.join(".dvs");
86 (config, dvs_dir)
87 }
88
89 #[derive(Default)]
93 pub struct Recorder {
94 pub seen: std::sync::Mutex<Vec<String>>,
95 pub rows: std::sync::Mutex<Vec<serde_json::Value>>,
96 cancel: Option<(crate::CancelToken, usize)>,
97 count: std::sync::atomic::AtomicUsize,
98 }
99
100 impl Recorder {
101 pub fn cancelling_on_transfer(token: crate::CancelToken, n: usize) -> Self {
103 Self {
104 cancel: Some((token, n)),
105 ..Self::default()
106 }
107 }
108
109 pub fn seen(&self) -> Vec<String> {
110 self.seen.lock().unwrap().clone()
111 }
112
113 fn push(&self, line: String) {
114 self.seen.lock().unwrap().push(line);
115 }
116
117 fn tick(&self) {
118 if let Some((token, n)) = &self.cancel {
119 if self.count.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1 == *n {
120 token.cancel();
121 }
122 }
123 }
124 }
125
126 impl crate::OperationObserver for Recorder {
127 fn planned(&self, p: &crate::OperationPlan) {
128 let (files, bytes) = (p.files_total, p.bytes_total);
129 let (to_files, to_bytes) = (p.files_to_transfer, p.bytes_to_transfer);
130 self.push(format!("plan {files} {bytes} {to_files} {to_bytes}"));
131 }
132
133 fn hashing_started(&self) {
134 self.push("hash start".to_string());
135 }
136
137 fn hashing_finished(&self) {
138 self.push("hash end".to_string());
139 }
140
141 fn transfer_started(&self, path: &Path, _total_bytes: u64) -> Option<crate::FileProgress> {
142 self.push(format!("transfer {}", path.display()));
143 self.tick();
144 None
145 }
146
147 fn file_finished(&self, row: crate::FileRow<'_>) {
148 let (path, json) = match row {
149 crate::FileRow::Add(r) => (&r.path, serde_json::to_value(r)),
150 crate::FileRow::Get(r) => (&r.path, serde_json::to_value(r)),
151 crate::FileRow::Status(r) => (&r.path, serde_json::to_value(r)),
152 };
153 self.push(format!("row {}", path.display()));
154 self.rows.lock().unwrap().push(json.unwrap());
155 }
156 }
157}