From 304d9f220cc208dd78fce11b703354ecd8d2168c Mon Sep 17 00:00:00 2001 From: Minijackson Date: Fri, 15 Nov 2019 18:14:07 +0100 Subject: add basic config file, use codemap_diagnostic for main errors --- src/cli.rs | 20 +++++++++++ src/config.rs | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 72 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 src/config.rs (limited to 'src') diff --git a/src/cli.rs b/src/cli.rs index e106de7..0a883af 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,5 +1,9 @@ +use crate::config::Config; + use structopt::StructOpt; +use std::path::PathBuf; + #[derive(Debug, Clone, StructOpt)] pub(crate) struct Cli { #[structopt(long, short, parse(from_occurrences))] @@ -8,11 +12,27 @@ pub(crate) struct Cli { #[structopt(long, number_of_values = 1, parse(try_from_str = shell_words::split))] pub(crate) extra_arg: Vec>, + #[structopt(long, short = "C", default_value = ".")] + pub(crate) directory: PathBuf, + #[structopt(subcommand)] pub(crate) command: Command, + + #[structopt(flatten)] + pub(crate) common_options: Config, } #[derive(Debug, Clone, StructOpt)] pub(crate) enum Command { Generate { file: String }, + Config { + #[structopt(subcommand)] + command: ConfigCommand, + } +} + +#[derive(Debug, Clone, StructOpt)] +pub(crate) enum ConfigCommand { + Default, + Show, } diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..4463479 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,107 @@ +use anyhow::{anyhow, Context, Result}; +use codemap::CodeMap; +use codemap_diagnostic::Diagnostic; +use serde_derive::{Deserialize, Serialize}; +use structopt::StructOpt; +use thiserror::Error; + +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +pub(super) const DEFAULT_PROJECT_CONFIGURATION_FILE_NAME: &str = "poseidoc.toml"; + +#[derive(Debug, Clone, StructOpt, Deserialize, Serialize)] +#[structopt(rename_all = "kebab-case")] +#[serde(rename_all = "kebab-case", default)] +pub(crate) struct Config { + #[structopt(skip)] + pub(crate) name: String, + #[structopt(long, default_value = ".")] + pub(crate) compile_commands_location: PathBuf, + #[structopt(skip = vec![])] + pub(crate) extra_clang_args: Vec, +} + +impl Default for Config { + fn default() -> Self { + Config { + name: "My Project".into(), + compile_commands_location: PathBuf::from(r"."), + extra_clang_args: Vec::new(), + } + } +} + +pub(super) fn load_config(location: impl AsRef, codemap: &mut CodeMap) -> Result { + let location = location.as_ref(); + + let final_path = if location.is_dir() { + location.join(DEFAULT_PROJECT_CONFIGURATION_FILE_NAME) + } else if !location.exists() { + return Err(anyhow!("File {:?} does not exists", location)) + .with_context(|| format!("Failed to open project configuration: {:?}", location)); + } else { + location.to_owned() + }; + + let config_str = + std::fs::read_to_string(&final_path).map_err(|err| ConfigLoadError::IoError { + file_name: final_path.clone(), + source: err, + })?; + Ok( + toml::from_str(&config_str).map_err(|err| ConfigLoadError::TomlDeserialzation { + file: codemap.add_file(final_path.to_string_lossy().into(), config_str), + source: err, + })?, + ) +} + +#[derive(Debug, Error)] +#[error("Error loading project configuration")] +pub(crate) enum ConfigLoadError { + #[error("Failed to read project configuration: {:?}", file_name)] + IoError { + file_name: PathBuf, + source: std::io::Error, + }, + #[error("Failed to parse project configuration: {:?}", file.name())] + TomlDeserialzation { + file: Arc, + source: toml::de::Error, + }, +} + +impl From<&ConfigLoadError> for Diagnostic { + fn from(err: &ConfigLoadError) -> Diagnostic { + use codemap_diagnostic::{Level, SpanLabel, SpanStyle}; + use std::convert::TryInto; + + let message = err.to_string(); + + let spans = match err { + ConfigLoadError::IoError { .. } => vec![], + ConfigLoadError::TomlDeserialzation { file, source, .. } => { + if let Some((line, col)) = source.line_col() { + let line_span = file.line_span(line); + let col = col.try_into().unwrap(); + let span = line_span.subspan(col, col); + vec![SpanLabel { + span, + label: None, + style: SpanStyle::Primary, + }] + } else { + vec![] + } + } + }; + + Diagnostic { + level: Level::Error, + message, + code: None, + spans, + } + } +} diff --git a/src/main.rs b/src/main.rs index e1d4752..503688e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ //mod doxygen; mod cli; +mod config; mod entities; mod generator; mod pandoc; @@ -8,14 +9,63 @@ mod parsing; #[macro_use] extern crate log; -use cli::Command; +use cli::{Command, ConfigCommand}; use generator::generate; use parsing::parse_file; -use anyhow::Result; +use anyhow::{Context, Result}; +use codemap::CodeMap; +use codemap_diagnostic::{Diagnostic, Emitter, Level}; use structopt::StructOpt; -fn main() -> Result<()> { +fn into_diagnostic(err: &anyhow::Error) -> Diagnostic { + if let Some(err) = err.downcast_ref::() { + err.into() + } else { + Diagnostic { + level: Level::Error, + message: err.to_string(), + code: None, + spans: vec![], + } + } +} + +fn main() { + let mut codemap = CodeMap::new(); + + match start(&mut codemap) { + Ok(()) => {} + Err(err) => { + let mut emitter = + Emitter::stderr(codemap_diagnostic::ColorConfig::Auto, Some(&codemap)); + + let mut diagnostics = Vec::new(); + diagnostics.push(into_diagnostic(&err)); + + if let Some(mut source) = err.source() { + diagnostics.push(Diagnostic { + level: Level::Note, + message: format!("Caused by: {}", source), + code: None, + spans: vec![], + }); + while let Some(source2) = source.source() { + source = source2; + diagnostics.push(Diagnostic { + level: Level::Note, + message: format!("Caused by: {}", source), + code: None, + spans: vec![], + }); + } + } + emitter.emit(&diagnostics); + } + } +} + +fn start(codemap: &mut CodeMap) -> Result<()> { let cli = cli::Cli::from_args(); pretty_env_logger::formatted_builder() .filter( @@ -30,6 +80,9 @@ fn main() -> Result<()> { ) .try_init()?; + std::env::set_current_dir(&cli.directory) + .with_context(|| format!("Cannot change current directory to: {:?}", cli.directory))?; + match cli.command { Command::Generate { file } => { let extra_args = cli.extra_arg.iter().flatten().map(AsRef::as_ref).collect(); @@ -38,6 +91,19 @@ fn main() -> Result<()> { let base_output_dir = std::path::Path::new("doc"); generate(&base_output_dir, manager)?; } + Command::Config { + command: ConfigCommand::Default, + } => { + print!("{}", toml::to_string_pretty(&config::Config::default())?); + } + Command::Config { + command: ConfigCommand::Show, + } => { + print!( + "{}", + toml::to_string_pretty(&config::load_config(".", codemap)?)? + ); + } } Ok(()) -- cgit v1.2.3