use serde::Deserialize;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::str::FromStr as _;
use crate::convention::Convention;
use crate::error::Error;
#[derive(Debug, Deserialize)]
struct CargoManifest {
package: Option<PackageSection>,
workspace: Option<WorkspaceSection>,
}
#[derive(Debug, Deserialize)]
struct PackageSection {
metadata: Option<MetadataSection>,
}
#[derive(Debug, Deserialize)]
struct WorkspaceSection {
metadata: Option<MetadataSection>,
}
#[derive(Debug, Deserialize)]
struct MetadataSection {
#[serde(rename = "convention-lint")]
convention_lint: Option<ConventionLintTable>,
}
#[derive(Debug, Deserialize)]
struct ConventionLintTable {
#[serde(flatten)]
rules: HashMap<String, String>,
dirs: Option<HashMap<String, Vec<PathBuf>>>,
}
#[derive(Debug, Default)]
pub struct Config {
pub rules: HashMap<String, Convention>,
pub dirs: HashMap<String, Vec<PathBuf>>,
}
pub fn load_config(manifest_path: &Path) -> Result<Config, Error> {
let content = std::fs::read_to_string(manifest_path).map_err(|source| Error::Io {
path: manifest_path.to_owned(),
source,
})?;
let manifest: CargoManifest = toml::from_str(&content).map_err(|source| Error::Toml {
path: manifest_path.to_owned(),
source,
})?;
let raw_lint = manifest
.package
.and_then(|p| p.metadata)
.and_then(|m| m.convention_lint)
.or_else(|| {
manifest
.workspace
.and_then(|w| w.metadata)
.and_then(|m| m.convention_lint)
})
.ok_or_else(|| Error::MissingSection(manifest_path.to_owned()))?;
let mut rules = HashMap::new();
for (ext, raw_conv) in raw_lint.rules {
if ext == "dirs" {
continue;
}
let conv = Convention::from_str(&raw_conv).map_err(|_| Error::UnknownConvention {
ext: ext.clone(),
value: raw_conv,
})?;
rules.insert(ext, conv);
}
Ok(Config {
rules,
dirs: raw_lint.dirs.unwrap_or_default(),
})
}