Created
July 21, 2018 21:55
-
-
Save stuarth/8ebd1a5e16052d1f9940a1d60587ee28 to your computer and use it in GitHub Desktop.
command_records.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| pub fn command<P: AsRef<Path>, P1: AsRef<Path>>( | |
| matches: &ArgMatches, | |
| repo: &Repository, | |
| mut config: Configuration, | |
| working_directory: P, | |
| config_path: P1, | |
| ) -> i32 { | |
| if !matches.is_present("no-author") && config.author.is_none() { | |
| if let Some(author) = | |
| cfg::Author::from_gitconfig(working_directory.as_ref().join(".git").join("config")) | |
| { | |
| config.author = Some(author); | |
| } else { | |
| if atty::is(atty::Stream::Stdin) { | |
| println!("SIT needs your authorship identity to be configured\n"); | |
| use question::{Answer, Question}; | |
| let name = loop { | |
| match Question::new("What is your name?").ask() { | |
| None => continue, | |
| Some(Answer::RESPONSE(value)) => { | |
| if value.trim() == "" { | |
| continue; | |
| } else { | |
| break value; | |
| } | |
| } | |
| Some(answer) => panic!("Invalid answer {:?}", answer), | |
| } | |
| }; | |
| let email = match Question::new("What is your e-mail address?") | |
| .clarification("optional") | |
| .ask() | |
| { | |
| None => None, | |
| Some(Answer::RESPONSE(value)) => { | |
| if value.trim() == "" { | |
| None | |
| } else { | |
| Some(value) | |
| } | |
| } | |
| Some(answer) => panic!("Invalid answer {:?}", answer), | |
| }; | |
| config.author = Some(cfg::Author { name, email }); | |
| let file = | |
| fs::File::create(config_path).expect("can't open config file for writing"); | |
| serde_json::to_writer_pretty(file, &config).expect("can't write config"); | |
| } else { | |
| eprintln!("SIT needs your authorship identity to be configured (supported sources: sit, git), or re-run this command in a terminal\n"); | |
| return 1; | |
| } | |
| } | |
| } | |
| let id = matches.value_of("id").unwrap(); | |
| match repo.item(id) { | |
| None => { | |
| eprintln!("Item {} not found", id); | |
| return 1; | |
| } | |
| Some(item) => { | |
| fn record_files( | |
| matches: &ArgMatches, | |
| utc: DateTime<Utc>, | |
| config: &Configuration, | |
| ) -> Result<BoxedOrderedFiles<'static>, io::Error> { | |
| let files = matches | |
| .values_of("FILES") | |
| .unwrap_or(clap::Values::default()); | |
| let files: OrderedFiles<_> = files | |
| .into_iter() | |
| .flat_map(move |name| { | |
| let path = PathBuf::from(&name); | |
| if path.is_file() { | |
| vec![(OsString::from(name), path)] | |
| } else if path.is_dir() { | |
| path.read_dir() | |
| .expect(&format!("error reading {}", path.display())) | |
| .filter_map(|f| { | |
| if let Ok(f) = f { | |
| if f.metadata().expect("error reading metadata").is_file() { | |
| Some(f) | |
| } else { | |
| None | |
| } | |
| } else { | |
| None | |
| } | |
| }) | |
| .map(|entry: ::std::fs::DirEntry| (entry.file_name(), entry.path())) | |
| .collect() | |
| } else { | |
| eprintln!( | |
| "{} does not exist or is not a regular file", | |
| path.to_str().unwrap() | |
| ); | |
| exit(1); | |
| } | |
| }) | |
| .map(|(name, path)| { | |
| let abs_name = | |
| ::dunce::canonicalize(path).expect("can't canonicalize path"); | |
| let cur_dir = ::dunce::canonicalize( | |
| env::current_dir().expect("can't get current directory"), | |
| ).expect("can't canonicalize current directory"); | |
| match abs_name.strip_prefix(&cur_dir) { | |
| Err(_) => { | |
| eprintln!( | |
| "Path {:?} is not relative to {} current directory", | |
| name, | |
| cur_dir.to_str().unwrap() | |
| ); | |
| exit(1); | |
| } | |
| Ok(path) => String::from(path.to_str().unwrap()), | |
| } | |
| }) | |
| .map(|name| { | |
| ( | |
| name.clone(), | |
| ::std::fs::File::open(name).expect("can't open file"), | |
| ) | |
| }) | |
| .into(); | |
| let types: Vec<_> = match matches.value_of("type") { | |
| Some(types) => types.split(",").collect(), | |
| None => vec![], | |
| }; | |
| let type_files: OrderedFiles<_> = types | |
| .iter() | |
| .map(|t| (format!(".type/{}", *t), &[][..])) | |
| .into(); | |
| // .authors | |
| let authorship_files: Option<OrderedFiles<(String, _)>> = | |
| if !matches.is_present("no-author") { | |
| let authors = format!("{}", config.author.clone().unwrap()); | |
| Some(vec![(String::from(".authors"), Cursor::new(authors))].into()) | |
| } else { | |
| None | |
| }; | |
| let timestamp: Option<OrderedFiles<(String, _)>> = | |
| if !matches.is_present("no-timestamp") { | |
| let timestamp = format!("{:?}", utc); | |
| Some(vec![(String::from(".timestamp"), Cursor::new(timestamp))].into()) | |
| } else { | |
| None | |
| }; | |
| Ok(files + type_files + authorship_files + timestamp) | |
| } | |
| let utc: DateTime<Utc> = Utc::now(); | |
| let signing = matches.is_present("sign") || config.signing.enabled; | |
| let files = record_files(matches, utc, &config).expect("failed collecting files"); | |
| let files = if signing { | |
| use std::ffi::OsString; | |
| let program = super::gnupg(matches, &config).expect("can't find GnuPG"); | |
| let key = match matches | |
| .value_of("signing-key") | |
| .map(String::from) | |
| .or_else(|| config.signing.key.clone()) | |
| { | |
| Some(key) => Some(OsString::from(key)), | |
| None => None, | |
| }; | |
| let mut command = ::std::process::Command::new(program); | |
| command | |
| .stdin(::std::process::Stdio::piped()) | |
| .stdout(::std::process::Stdio::piped()) | |
| .arg("--sign") | |
| .arg("--armor") | |
| .arg("--detach-sign") | |
| .arg("-o") | |
| .arg("-"); | |
| if key.is_some() { | |
| let _ = command.arg("--default-key").arg(key.unwrap()); | |
| } | |
| let mut child = command.spawn().expect("failed spawning gnupg"); | |
| { | |
| let mut stdin = child.stdin.as_mut().expect("Failed to open stdin"); | |
| let mut hasher = repo.config().hashing_algorithm().hasher(); | |
| files.hash(&mut *hasher).expect("failed hashing files"); | |
| let hash = hasher.result_box(); | |
| let encoded_hash = repo.config().encoding().encode(&hash); | |
| stdin | |
| .write_all(encoded_hash.as_bytes()) | |
| .expect("Failed to write to stdin"); | |
| } | |
| let output = child.wait_with_output().expect("failed to read stdout"); | |
| if !output.status.success() { | |
| eprintln!("Error: {}", String::from_utf8_lossy(&output.stderr)); | |
| return 1; | |
| } else { | |
| let files = | |
| record_files(matches, utc, &config).expect("failed collecting files"); | |
| let signature_file: OrderedFiles<(String, _)> = | |
| vec![(String::from(".signature"), Cursor::new(output.stdout))].into(); | |
| files + signature_file | |
| } | |
| } else { | |
| files | |
| }; | |
| let record = item.new_record(files, true).expect("can't create a record"); | |
| println!("{}", record.encoded_hash()); | |
| } | |
| } | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment