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, } } }