1use serde::Deserialize;
2use std::path::{Path, PathBuf};
3
4#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq)]
5pub struct FileConfig {
6 pub format: Option<String>,
7 pub baseline: Option<PathBuf>,
8 pub md_out: Option<PathBuf>,
9 pub profile: Option<String>,
10 pub fail_on: Option<String>,
11 pub timings: Option<String>,
12 pub include: Option<Vec<String>>,
13 pub exclude: Option<Vec<String>>,
14}
15
16#[derive(Debug, Clone, Default, PartialEq, Eq)]
17pub struct CliOverrides {
18 pub format: Option<String>,
19 pub baseline: Option<PathBuf>,
20 pub md_out: Option<PathBuf>,
21 pub profile: Option<String>,
22 pub fail_on: Option<String>,
23 pub timings: Option<String>,
24 pub include: Vec<String>,
25 pub exclude: Vec<String>,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct RuntimeConfig {
30 pub format: String,
31 pub baseline: Option<PathBuf>,
32 pub md_out: Option<PathBuf>,
33 pub profile: String,
34 pub fail_on: String,
35 pub timings: String,
36 pub include: Vec<String>,
37 pub exclude: Vec<String>,
38}
39
40pub fn load_file_config(config_path: Option<&Path>) -> Result<FileConfig, miette::Report> {
41 let Some(path) = resolve_config_path(config_path) else {
42 return Ok(FileConfig::default());
43 };
44
45 let raw = std::fs::read_to_string(&path)
46 .map_err(|err| miette::miette!("Failed to read config file {}: {}", path.display(), err))?;
47 toml::from_str(&raw)
48 .map_err(|err| miette::miette!("Failed to parse config file {}: {}", path.display(), err))
49}
50
51pub fn resolve_runtime_config(file: FileConfig, cli: CliOverrides) -> RuntimeConfig {
52 RuntimeConfig {
53 format: cli
54 .format
55 .or(file.format)
56 .unwrap_or_else(|| "table".to_string()),
57 baseline: cli.baseline.or(file.baseline),
58 md_out: cli.md_out.or(file.md_out),
59 profile: cli
60 .profile
61 .or(file.profile)
62 .unwrap_or_else(|| "full".to_string()),
63 fail_on: cli
64 .fail_on
65 .or(file.fail_on)
66 .unwrap_or_else(|| "error".to_string()),
67 timings: cli
68 .timings
69 .or(file.timings)
70 .unwrap_or_else(|| "off".to_string()),
71 include: if cli.include.is_empty() {
72 file.include.unwrap_or_default()
73 } else {
74 cli.include
75 },
76 exclude: if cli.exclude.is_empty() {
77 file.exclude.unwrap_or_default()
78 } else {
79 cli.exclude
80 },
81 }
82}
83
84fn resolve_config_path(config_path: Option<&Path>) -> Option<PathBuf> {
85 if let Some(path) = config_path {
86 return Some(path.to_path_buf());
87 }
88
89 let default = PathBuf::from("verifyos.toml");
90 if default.exists() {
91 Some(default)
92 } else {
93 None
94 }
95}