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
|
use clap::Subcommand;
use eyre::Result;
use crate::atuin_client::{
database::ClientSqlite, record::sqlite_store::SqliteStore, settings::Settings,
};
use itertools::Itertools;
use time::{OffsetDateTime, UtcOffset};
mod pull;
mod purge;
mod push;
mod rebuild;
mod rekey;
mod verify;
#[derive(Subcommand, Debug)]
#[command(infer_subcommands = true)]
pub(crate) enum Cmd {
/// Print the current status of the record store
Status,
/// Rebuild a store (eg atuin store rebuild history)
Rebuild(rebuild::Rebuild),
/// Re-encrypt the store with a new key (potential for data loss!)
Rekey(rekey::Rekey),
/// Delete all records in the store that cannot be decrypted with the current key
Purge(purge::Purge),
/// Verify that all records in the store can be decrypted with the current key
Verify(verify::Verify),
/// Push all records to the remote sync server (one way sync)
Push(push::Push),
/// Pull records from the remote sync server (one way sync)
Pull(pull::Pull),
}
impl Cmd {
pub(crate) async fn run(
&self,
settings: &Settings,
database: &ClientSqlite,
store: SqliteStore,
) -> Result<()> {
match self {
Self::Status => self.status(store).await,
Self::Rebuild(rebuild) => rebuild.run(settings, store, database).await,
Self::Rekey(rekey) => rekey.run(settings, store).await,
Self::Verify(verify) => verify.run(settings, store).await,
Self::Purge(purge) => purge.run(settings, store).await,
Self::Push(push) => push.run(settings, store).await,
Self::Pull(pull) => pull.run(settings, store, database).await,
}
}
pub(crate) async fn status(&self, store: SqliteStore) -> Result<()> {
let host_id = Settings::host_id().await?;
let offset = UtcOffset::current_local_offset().unwrap_or(UtcOffset::UTC);
let status = store.status().await?;
// TODO: should probs build some data structure and then pretty-print it or smth
for (host, st) in status.hosts.iter().sorted_by_key(|(h, _)| *h) {
let host_string = if host == &host_id {
format!("host: {} <- CURRENT HOST", host.0.as_hyphenated())
} else {
format!("host: {}", host.0.as_hyphenated())
};
println!("{host_string}");
for (tag, idx) in st.iter().sorted_by_key(|(tag, _)| *tag) {
println!("\tstore: {tag}");
let first = store.first(*host, tag).await?;
let last = store.last(*host, tag).await?;
println!("\t\tidx: {idx}");
if let Some(first) = first {
println!("\t\tfirst: {}", first.id.0.as_hyphenated());
let time =
OffsetDateTime::from_unix_timestamp_nanos(i128::from(first.timestamp))?
.to_offset(offset);
println!("\t\t\tcreated: {time}");
}
if let Some(last) = last {
println!("\t\tlast: {}", last.id.0.as_hyphenated());
let time =
OffsetDateTime::from_unix_timestamp_nanos(i128::from(last.timestamp))?
.to_offset(offset);
println!("\t\t\tcreated: {time}");
}
}
println!();
}
Ok(())
}
}
|