use crate::util::common_options::ProbeOptions;
use crate::util::rtt;
use crate::{FormatOptions, cmd::dap_server::DebuggerError};
use anyhow::{Result, anyhow};
use probe_rs::probe::{DebugProbeSelector, WireProtocol};
use serde::{Deserialize, Serialize};
use std::{env::current_dir, path::PathBuf};
use super::startup::TargetSessionType;
#[derive(Clone, Serialize, Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct SessionConfig {
#[serde(default = "default_console_log")]
pub(crate) console_log_level: Option<ConsoleLog>,
pub(crate) cwd: Option<PathBuf>,
pub(crate) probe: Option<DebugProbeSelector>,
pub(crate) chip: Option<String>,
pub(crate) chip_description_path: Option<PathBuf>,
#[serde(default)]
pub(crate) connect_under_reset: bool,
pub(crate) speed: Option<u32>,
pub(crate) wire_protocol: Option<WireProtocol>,
#[serde(default)]
pub(crate) allow_erase_all: bool,
#[serde(default)]
pub(crate) flashing_config: FlashingConfig,
pub(crate) core_configs: Vec<CoreConfig>,
}
impl SessionConfig {
pub(crate) fn validate_configuration_option_compatibility(
&self,
requested_target_session_type: TargetSessionType,
) -> Result<(), DebuggerError> {
if requested_target_session_type == TargetSessionType::AttachRequest
&& (self.flashing_config.flashing_enabled
|| self.flashing_config.halt_after_reset
|| self.flashing_config.full_chip_erase
|| self.flashing_config.restore_unwritten_bytes)
{
let message = "Please do not use any of the `flashing_enabled`, `reset_after_flashing`, halt_after_reset`, `full_chip_erase`, or `restore_unwritten_bytes` options when using `attach` request type.";
return Err(DebuggerError::Other(anyhow!(message)));
}
Ok(())
}
pub(crate) fn validate_config_files(&mut self) -> Result<(), DebuggerError> {
self.cwd = self.resolve_cwd()?;
for target_core_config in &mut self.core_configs {
target_core_config.program_binary = match get_absolute_path(
self.cwd.as_ref(),
target_core_config.program_binary.as_ref(),
) {
Ok(program_binary) => {
if !program_binary.is_file() {
return Err(DebuggerError::Other(anyhow!(
"Invalid program binary file specified '{}'",
program_binary.display()
)));
}
Some(program_binary)
}
Err(error) => {
return Err(DebuggerError::Other(anyhow!(
"Please use the `program-binary` option to specify an executable for this target core. {error:?}"
)));
}
};
target_core_config.svd_file =
match get_absolute_path(self.cwd.as_ref(), target_core_config.svd_file.as_ref()) {
Ok(svd_file) => {
if !svd_file.is_file() {
tracing::warn!("SVD file {} not found.", svd_file.display());
None
} else {
Some(svd_file)
}
}
Err(error) => {
tracing::debug!("SVD file not specified: {:?}", error);
None
}
};
}
self.chip_description_path =
match get_absolute_path(self.cwd.as_ref(), self.chip_description_path.as_ref()) {
Ok(description) => {
if !description.is_file() {
return Err(DebuggerError::Other(anyhow!(
"Invalid chip description file specified '{}'",
description.display()
)));
}
Some(description)
}
Err(error) => {
tracing::debug!("Chip description file not specified: {:?}", error);
None
}
};
Ok(())
}
pub(crate) fn resolve_cwd(&self) -> Result<Option<PathBuf>, DebuggerError> {
let path = match self.cwd {
Some(ref temp_path) if temp_path.is_dir() => Some(temp_path.to_path_buf()),
_ => {
if let Ok(current_dir) = current_dir() {
Some(current_dir)
} else {
tracing::error!(
"Cannot use current working directory. Please check existence and permissions."
);
None
}
}
};
Ok(path)
}
pub(crate) fn probe_options(&self) -> ProbeOptions {
ProbeOptions {
chip: self.chip.clone(),
chip_description_path: self.chip_description_path.clone(),
protocol: self.wire_protocol,
non_interactive: true,
probe: self.probe.clone(),
speed: self.speed,
connect_under_reset: self.connect_under_reset,
dry_run: false,
allow_erase_all: self.allow_erase_all,
}
}
}
fn get_absolute_path(
configured_cwd: Option<&PathBuf>,
os_file_to_validate: Option<&PathBuf>,
) -> Result<PathBuf, DebuggerError> {
match os_file_to_validate {
Some(temp_path) => {
let mut new_path = PathBuf::new();
if temp_path.is_relative() {
if let Some(cwd_path) = configured_cwd {
new_path.push(cwd_path);
} else {
return Err(DebuggerError::Other(anyhow!(
"Invalid value {:?} for `cwd`",
configured_cwd
)));
}
}
new_path.push(temp_path);
Ok(new_path)
}
None => Err(DebuggerError::Other(anyhow!("Missing value for file."))),
}
}
#[derive(Clone, Serialize, Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct FlashingConfig {
#[serde(default)]
pub(crate) flashing_enabled: bool,
#[serde(default)]
pub(crate) halt_after_reset: bool,
#[serde(default)]
pub(crate) verify_before_flashing: bool,
#[serde(default)]
pub(crate) full_chip_erase: bool,
#[serde(default)]
pub(crate) restore_unwritten_bytes: bool,
#[serde(default)]
pub(crate) verify_after_flashing: bool,
#[serde(default)]
pub(crate) format_options: FormatOptions,
}
#[derive(Clone, Serialize, Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct CoreConfig {
#[serde(default)]
pub(crate) core_index: usize,
pub(crate) program_binary: Option<PathBuf>,
pub(crate) svd_file: Option<PathBuf>,
#[serde(flatten)]
pub(crate) rtt_config: rtt::RttConfig,
#[serde(default = "default_true")]
pub(crate) catch_reset: bool,
#[serde(default = "default_true")]
pub(crate) catch_hardfault: bool,
}
fn default_console_log() -> Option<ConsoleLog> {
Some(ConsoleLog::Console)
}
fn default_true() -> bool {
true
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
pub enum ConsoleLog {
Console,
Info,
Debug,
}
impl std::str::FromStr for ConsoleLog {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match &s.to_ascii_lowercase()[..] {
"console" => Ok(ConsoleLog::Console),
"info" => Ok(ConsoleLog::Info),
"debug" => Ok(ConsoleLog::Debug),
_ => Err(format!(
"'{s}' is not a valid console log level. Choose from [console, info, or debug]."
)),
}
}
}