use std::fmt; use std::fs::{read_to_string, write}; use std::io::{Error as IoError, Result as IoResult}; use std::path::PathBuf; use std::str::FromStr; use wmap_renderer::{Configuration, StageType, render_to_png, render_to_svg}; use thiserror::Error; #[derive(Debug)] enum ImageType { Png, Svg, } impl fmt::Display for ImageType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { ImageType::Png => write!(f, "png"), ImageType::Svg => write!(f, "svg"), } } } #[derive(Error, Debug)] pub enum WmapError { #[error("unknown image type.")] UnknownImageType, #[error("unknown stage type.")] UnknownStageType, } impl FromStr for ImageType { type Err = WmapError; fn from_str(s: &str) -> Result { match s.to_lowercase().as_str() { "png" => Ok(ImageType::Png), "svg" => Ok(ImageType::Svg), _ => Err(Self::Err::UnknownImageType), } } } fn stage_from_str(s: &str) -> Option { match s.to_lowercase().as_str() { "activities" => Some(StageType::Activities), "behavior" => Some(StageType::Behavior), "certainty" => Some(StageType::Certainty), "comparison" => Some(StageType::Comparison), "cynefin" => Some(StageType::Cynefin), "data" => Some(StageType::Data), "decision_drivers" => Some(StageType::DecisionDrivers), "efficiency" => Some(StageType::Efficiency), "failure" => Some(StageType::Failure), "focus_of_value" => Some(StageType::FocusOfValue), "knowledge" => Some(StageType::Knowledge), "knowledge_management" => Some(StageType::KnowledgeManagement), "market" => Some(StageType::Market), "market_action" => Some(StageType::MarketAction), "market_perception" => Some(StageType::MarketPerception), "perception_in_industry" => Some(StageType::PerceptionInIndustry), "practice" => Some(StageType::Practice), "publication_types" => Some(StageType::PublicationTypes), "ubiquity" => Some(StageType::Ubiquity), "understanding" => Some(StageType::Understanding), "user_perception" => Some(StageType::UserPerception), "evolution_stage" => Some(StageType::EvolutionStage), _ => None, } } struct Arguments { input: PathBuf, output: PathBuf, stage: StageType, image_type: ImageType, } fn print_help() { println!("Usage: wmap [-t|--type IMAGE_TYPE] [-s|--stage STAGE] [-o|--output OUTPUT_FILE] INPUT_FILE"); } fn parse_arguments() -> Result { use lexopt::prelude::*; let mut input = None; let mut output: String = String::new(); let mut stage = StageType::Activities; let mut image_type = ImageType::Png; let mut argument_parser = lexopt::Parser::from_env(); while let Some(argument) = argument_parser.next()? { match argument { Short('t') | Long("type") => { image_type = argument_parser.value()?.parse()?; } Short('s') | Long("stage") => { let stage_string: String = argument_parser.value()?.parse()?; stage = stage_from_str(&stage_string).ok_or("Invalid stage name")?; } Short('o') | Long("output") => { output = argument_parser.value()?.parse()?; } Value(value) if input.is_none() => { input = Some(value.string()?); } Short('h') | Long("help") => { print_help(); std::process::exit(0); } Short('v') | Long("version") => { let version = env!("CARGO_PKG_VERSION"); println!("{version}"); std::process::exit(0); } _ => return Err(argument.unexpected()), } } let input = PathBuf::from(input.ok_or("missing argument INPUT_FILE")?); let output = if output.is_empty() { input.with_extension(image_type.to_string()) } else { PathBuf::from(output) }; Ok(Arguments { input, output, stage, image_type, }) } fn run() -> IoResult<()> { let arguments = parse_arguments().map_err(|_| IoError::other("Unable to parse arguments"))?; arguments.input.try_exists()?; let source = read_to_string(arguments.input)?; let map = wmap_parser::parse(&source); let configuration = Configuration::default(); let image_data = match arguments.image_type { ImageType::Png => render_to_png(&map, arguments.stage, &configuration) .map_err(|_| IoError::other("Unable to render PNG"))?, ImageType::Svg => render_to_svg(&map, arguments.stage, &configuration) .map_err(|_| IoError::other("Unable to render SVG"))? .into_bytes(), }; write(arguments.output, image_data)?; Ok(()) } fn main() -> IoResult<()> { let result = run(); if cfg!(debug_assertions) { result } else { match result { Ok(()) => Ok(()), Err(e) => { eprintln!("Error: {e}"); eprintln!(); print_help(); std::process::exit(1); } } } }