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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
// Free Software under GPL-3.0, see LICENSE
// Copyright 2017 Bryan Newbold
extern crate clap;
extern crate env_logger;
#[macro_use]
extern crate error_chain;
extern crate geniza;
extern crate sodiumoxide;
// TODO: more careful import
use geniza::*;
use std::path::{Path, PathBuf};
use clap::{App, SubCommand};
use std::env::current_dir;
use sodiumoxide::crypto::stream::Key;
// Helper to find a dat directory somewhere in the parent to the current working directory (or None
// if not found)
fn find_dat_dir() -> Option<PathBuf> {
let mut here: &Path = ¤t_dir().unwrap();
loop {
let check = here.join(".dat");
if check.is_dir() && check.join("metadata.tree").is_file() {
return Some(check);
};
here = match here.parent() {
None => return None,
Some(t) => t,
}
}
}
fn run() -> Result<()> {
env_logger::init().unwrap();
let matches = App::new("geniza")
.version(env!("CARGO_PKG_VERSION"))
.subcommand(
SubCommand::with_name("clone")
.about("Finds and downloads a dat archive from the network into a given folder")
.arg_from_usage("<address> 'dat address (public key) to fetch'")
.arg_from_usage("[dir] 'directory to clone into'")
.arg_from_usage("--full 'pull and save complete history (not just latest version)'"),
)
.subcommand(
SubCommand::with_name("init")
.about("Creates a data archive in the current directory")
.arg_from_usage("[dir] 'init somewhere other than current directory'"),
)
.subcommand(
SubCommand::with_name("status")
.about("Displays current status of archive and checkout")
)
.subcommand(
SubCommand::with_name("log")
.about("Displays version history of the archive")
)
.subcommand(
SubCommand::with_name("checkout")
.about("Copies (or overwrites) files from dat archive")
.arg_from_usage("<path> 'relative path to checkout'"),
)
.subcommand(
SubCommand::with_name("add")
.about("Adds a path to the current dat archive")
.arg_from_usage("<path> 'file to delete from dat archive'"),
)
.subcommand(
SubCommand::with_name("rm")
.about("Removes a path from the current dat archive, and from disk (danger!)")
.arg_from_usage("<path> 'file to delete from dat archive'"),
)
.subcommand(
SubCommand::with_name("ls")
.about("Lists contents of the archive")
.arg_from_usage("[path] 'path to display'")
.arg_from_usage("--recursive 'show directory recursively'"),
)
.subcommand(
SubCommand::with_name("seed")
.about("Uploads indefinately to any peer")
)
.subcommand(
SubCommand::with_name("pull")
.about("Pulls highest known version from all possible peers")
.arg_from_usage("--forever 'continues to search for updates forever'"),
)
.get_matches();
match matches.subcommand() {
("clone", Some(subm)) => {
let dat_key = subm.value_of("address").unwrap();
let key_bytes = parse_dat_address(&dat_key)?;
let key = Key::from_slice(&key_bytes).unwrap();
let dir = Path::new(subm.value_of("dir").unwrap());
let mut sync = Synchronizer::new_downloader(key,
SyncMode::RxMax,
dir)?;
let peer_count = sync.discover()?;
println!("Found {} potential peers", peer_count);
sync.run()?;
}
("init", Some(subm)) => {
let _dir = Path::new(subm.value_of("dir").unwrap());
unimplemented!();
}
("status", Some(_subm)) => {
unimplemented!();
}
("log", Some(_subm)) => {
let dat_dir = match find_dat_dir() {
Some(p) => p,
None => {
println!("Couldn't find '.dat/' in the current or (any parent) directory.");
println!("Are you running from inside a Dat archive?");
::std::process::exit(-1);
}
};
println!("{:?}", dat_dir);
let mut drive = DatDrive::open(dat_dir, false)?;
for entry in drive.history(0) {
let entry = entry?;
if let Some(stat) = entry.stat {
if stat.get_blocks() == 0 {
println!("{}\t[chg] {}",
entry.index, entry.path.display());
} else {
println!("{}\t[put] {}\t{} bytes ({} blocks)",
entry.index, entry.path.display(), stat.get_size(), stat.get_blocks());
}
} else {
println!("{}\t[del] {}",
entry.index, entry.path.display());
}
}
}
("checkout", Some(subm)) => {
let _path = Path::new(subm.value_of("path").unwrap());
unimplemented!();
}
("add", Some(subm)) => {
let _path = Path::new(subm.value_of("path").unwrap());
unimplemented!();
}
("rm", Some(subm)) => {
let _path = Path::new(subm.value_of("path").unwrap());
unimplemented!();
}
("ls", Some(subm)) => {
let _path = Path::new(subm.value_of("path").unwrap());
unimplemented!();
}
("seed", Some(subm)) => {
let _path = Path::new(subm.value_of("path").unwrap());
unimplemented!();
}
("pull", Some(subm)) => {
let _path = Path::new(subm.value_of("path").unwrap());
unimplemented!();
}
_ => {
println!("Missing or unimplemented command!");
println!("{}", matches.usage());
::std::process::exit(-1);
}
}
Ok(())
}
quick_main!(run);
|