use anyhow::Result; use std::convert::TryFrom; use std::path::{Path, PathBuf}; use std::str::FromStr; use serde::Serialize; use structopt::StructOpt; use subplot::Document; #[derive(Debug)] enum OutputFormat { Plain, Json, } impl FromStr for OutputFormat { type Err = String; fn from_str(s: &str) -> std::result::Result { match s.to_ascii_lowercase().as_ref() { "plain" => Ok(OutputFormat::Plain), "json" => Ok(OutputFormat::Json), _ => Err(format!("Unknown output format: `{}`", s)), } } } #[derive(Debug, StructOpt)] #[structopt(name = "sp-meta", about = "Show Subplot document metadata.")] struct Opt { /// Form that you want the output to take #[structopt(short = "o", default_value = "plain", possible_values=&["plain", "json"])] output_format: OutputFormat, /// Input subplot document filename #[structopt(parse(from_os_str))] filename: PathBuf, } #[derive(Serialize)] struct Metadata { sources: Vec, title: String, binding_files: Vec, function_files: Vec, bibliographies: Vec, scenarios: Vec, } impl TryFrom<&mut Document> for Metadata { type Error = subplot::SubplotError; fn try_from(doc: &mut Document) -> std::result::Result { let sources: Vec<_> = doc .sources() .into_iter() .map(|p| filename(Some(&p))) .collect(); let title = doc.meta().title().to_owned(); let binding_files = doc .meta() .bindings_filenames() .into_iter() .map(|p| filename(Some(&p))) .collect(); let function_files = doc .meta() .functions_filenames() .into_iter() .map(|p| filename(Some(&p))) .collect(); let bibliographies = doc .meta() .bibliographies() .into_iter() .map(|p| filename(Some(&p))) .collect(); let scenarios = doc .scenarios()? .into_iter() .map(|s| s.title().to_owned()) .collect(); Ok(Self { sources, title, binding_files, function_files, bibliographies, scenarios, }) } } impl Metadata { fn write_list(v: &[String], prefix: &str) { v.iter().for_each(|entry| println!("{}: {}", prefix, entry)) } fn write_out(&self) { Self::write_list(&self.sources, "source"); println!("title: {}", self.title); Self::write_list(&self.binding_files, "bindings"); Self::write_list(&self.function_files, "functions"); Self::write_list(&self.bibliographies, "bibliography"); Self::write_list(&self.scenarios, "scenario"); } } fn main() -> Result<()> { let opt = Opt::from_args(); let basedir = subplot::get_basedir_from(&opt.filename)?; let mut doc = Document::from_file(&basedir, &opt.filename)?; let meta = Metadata::try_from(&mut doc)?; match opt.output_format { OutputFormat::Plain => meta.write_out(), OutputFormat::Json => println!("{}", serde_json::to_string_pretty(&meta)?), } Ok(()) } fn filename(name: Option<&Path>) -> String { let path = match name { None => return "".to_string(), Some(x) => x, }; match path.to_str() { None => "non-UTF8 filename".to_string(), Some(x) => x.to_string(), } }