use std::collections::HashMap;
use crate::rttui::channel::ChannelConfig;
use anyhow::{bail, Context};
use probe_rs::WireProtocol;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Deserialize, Serialize)]
pub struct Configs(HashMap<String, Config>);
#[derive(Debug, Deserialize, Serialize)]
pub struct Config {
pub general: General,
pub flashing: Flashing,
pub reset: Reset,
pub probe: Probe,
pub rtt: Rtt,
pub gdb: Gdb,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Probe {
pub usb_vid: Option<String>,
pub usb_pid: Option<String>,
pub serial: Option<String>,
pub protocol: WireProtocol,
pub speed: Option<u32>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Flashing {
pub enabled: bool,
#[deprecated(
since = "0.9.0",
note = "The 'halt_afterwards' key has moved to the 'reset' section"
)]
pub halt_afterwards: bool,
pub restore_unwritten_bytes: bool,
pub flash_layout_output_path: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Reset {
pub enabled: bool,
pub halt_afterwards: bool,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct General {
pub chip: Option<String>,
pub chip_descriptions: Vec<String>,
pub log_level: log::Level,
pub derives: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Rtt {
pub enabled: bool,
pub channels: Vec<ChannelConfig>,
pub timeout: usize,
pub show_timestamps: bool,
pub log_enabled: bool,
pub log_path: PathBuf,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Gdb {
pub enabled: bool,
pub gdb_connection_string: Option<String>,
}
impl Configs {
pub fn new(name: impl AsRef<str>) -> anyhow::Result<Config> {
let mut s = config::Config::new();
s.merge(config::File::from_str(
include_str!("default.toml"),
config::FileFormat::Toml,
))?;
let config_files = [
".embed",
"Embed",
".embed.local",
"Embed.local",
".embed.local.ext",
"Embed.local.ext",
];
for file in &config_files {
s.merge(config::File::with_name(file).required(false))
.with_context(|| format!("Failed to merge config file '{}", file))?;
}
let map: HashMap<String, serde_json::value::Value> = s.try_into()?;
let config = match map.get(name.as_ref()) {
Some(c) => c,
None => bail!(
"Cannot find config \"{}\" (available configs: {})",
name.as_ref(),
map.keys().cloned().collect::<Vec<String>>().join(", "),
),
};
let mut s = config::Config::new();
Self::apply(name.as_ref(), &mut s, config, &map)?;
Ok(s.try_into()?)
}
pub fn apply(
name: &str,
s: &mut config::Config,
config: &serde_json::value::Value,
map: &HashMap<String, serde_json::value::Value>,
) -> Result<(), config::ConfigError> {
if let Some(derives) = config
.get("general")
.and_then(|g| g.get("derives").and_then(|d| d.as_str()))
.or(Some("default"))
{
if derives == name {
log::warn!("Endless recursion within the {} config.", derives);
} else if let Some(dconfig) = map.get(derives) {
Self::apply(derives, s, dconfig, map)?;
}
}
s.merge(config::File::from_str(
&serde_json::to_string(&config).unwrap(),
config::FileFormat::Json,
))
.map(|_| ())
}
}
#[cfg(test)]
mod test {
use super::Configs;
#[test]
fn default_config() {
let _config = Configs::new("default").unwrap();
}
}