apparently i lost that old source code so here we go again
diff --git a/Cargo.toml b/Cargo.toml
index cdad101..5147943 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,18 +20,29 @@
name = "tuf"
path = "./src/lib.rs"
+[[bin]]
+name = "tuf"
+path = "./src/bin/tuf.rs"
+required-features = ["cli"]
+
+[features]
+cli = ["clap", "lazy_static"]
+
[dependencies]
chrono = { version = "0.4", features = [ "serde" ] }
+clap = { version = "2", optional = true }
data-encoding = "2.0.0-rc.2"
derp = "0.0.10"
hyper = "0.10"
itoa = "0.4"
+lazy_static = { version = "1", optional = true }
log = "0.4"
ring = { version = "0.12", features = [ "rsa_signing" ] }
serde = "1"
serde_derive = "1"
serde_json = "1"
tempfile = "3"
+toml = "0.4"
untrusted = "0.5"
[dev-dependencies]
diff --git a/src/bin/tuf.rs b/src/bin/tuf.rs
new file mode 100644
index 0000000..fae30c8
--- /dev/null
+++ b/src/bin/tuf.rs
@@ -0,0 +1,117 @@
+extern crate clap;
+extern crate hyper;
+#[macro_use]
+extern crate lazy_static;
+extern crate serde;
+#[macro_use]
+extern crate serde_derive;
+extern crate toml;
+extern crate tuf;
+
+use clap::{App, ArgMatches, Arg, AppSettings, SubCommand};
+use hyper::client::Client as HttpClient;
+use std::env;
+use std::ffi::OsStr;
+use std::fs::File;
+use std::io::{self, Write, Read};
+use std::path::PathBuf;
+
+use tuf::Result;
+use tuf::error::Error;
+use tuf::client::{Config, Client, DefaultTranslator};
+use tuf::interchange::Json;
+use tuf::metadata::TargetPath;
+use tuf::repository::{FileSystemRepository, HttpRepository};
+
+lazy_static! {
+ static ref HOME_DIR: Option<PathBuf> = env::home_dir().map(|p| p.join(".tuf"));
+}
+
+#[derive(Deserialize)]
+struct CliConfig {
+ config_version: u32,
+ remote: String,
+}
+
+fn main() {
+ match run_main(parser().get_matches()) {
+ Ok(()) => std::process::exit(0),
+ Err(e) => {
+ writeln!(&mut io::stderr(), "{:?}", e).unwrap();
+ std::process::exit(1);
+ }
+ }
+}
+
+fn parser<'a, 'b>() -> App<'a, 'b> {
+ App::new("tuf")
+ .version(env!("CARGO_PKG_VERSION"))
+ .about("Authenticate and download packages")
+ .settings(&[AppSettings::SubcommandRequiredElseHelp])
+ .arg({
+ let arg = Arg::with_name("home")
+ .help("Set the TUF home directory")
+ .short("H")
+ .long("home")
+ .takes_value(true);
+
+ // TODO is this a sane way to do this?
+ match *HOME_DIR {
+ Some(ref home) => arg.default_value_os(home.as_os_str()),
+ None => arg.required(true),
+ }
+ })
+ .subcommand(
+ SubCommand::with_name("fetch")
+ .about("Fetch a target.")
+ .arg(Arg::with_name("target")
+ .help("Name of the target")
+ .takes_value(true)
+ .required(true)
+ )
+ )
+
+}
+
+fn run_main(matches: ArgMatches) -> Result<()> {
+ let home = matches.value_of_os("home").unwrap();
+
+ let mut buf = Vec::new();
+ let mut file = File::open(PathBuf::from(home).join("config.toml"))?;
+ file.read_to_end(&mut buf)?;
+ let cli_config: CliConfig = toml::from_slice(&buf).unwrap(); // TODO unwrap
+
+ if cli_config.config_version > 1 {
+ return Err(Error::Opaque(format!("Invalid config version: {}", cli_config.config_version)))
+ }
+
+ match matches.subcommand() {
+ ("fetch", Some(args)) => fetch_target(home, cli_config, args),
+ _ => unreachable!(),
+ }
+}
+
+fn fetch_target(home: &OsStr, cli_config: CliConfig, args: &ArgMatches) -> Result<()> {
+ let mut client = init_client(home, &cli_config)?;
+ client.update_local()?;
+ client.update_remote()?;
+ client.fetch_target(&TargetPath::new(args.value_of("target").unwrap().to_string())?)?;
+ Ok(())
+}
+
+fn init_client(home: &OsStr, cli_config: &CliConfig) -> Result<Client<Json, FileSystemRepository<Json>, HttpRepository<Json>, DefaultTranslator>> {
+ let config = Config::default();
+ let local = FileSystemRepository::<Json>::new(PathBuf::from(home));
+ let remote = HttpRepository::<Json>::new(cli_config.remote.parse()?, HttpClient::new(), None, None);
+ Client::new(config, local, remote)
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_parser() {
+ let _ = parser();
+ }
+}