use std::io;
use thiserror::Error;
pub type AppResult<T> = Result<T, AppError>;
#[derive(Error, Debug)]
pub enum AppError {
#[error("IO error: {0}")]
Io(#[from] io::Error),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error(transparent)]
Thunderstore(#[from] thunderstore_engine::Error),
#[error("{0}")]
Other(String),
#[error("Config serialization error: {0}")]
ConfigSerialization(String),
}
pub fn advice_message(headline: &str, detail: &str, next: &[&str]) -> String {
let mut rendered = String::from(headline);
if !detail.is_empty() {
rendered.push_str("\n\n");
rendered.push_str(detail);
}
if !next.is_empty() {
rendered.push('\n');
for command in next {
rendered.push_str("\n ");
rendered.push_str(command);
}
}
rendered
}
impl AppError {
pub fn advice(headline: impl Into<String>, detail: impl Into<String>, next: &[&str]) -> Self {
AppError::Other(advice_message(&headline.into(), &detail.into(), next))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn advice_renders_headline_detail_and_indented_commands() {
let message = advice_message(
"there's nothing to migrate.",
"Your config has no `mod_list` key.",
&["vmm install <Owner-ModName>"],
);
assert_eq!(
message,
"there's nothing to migrate.\n\nYour config has no `mod_list` key.\n\n vmm install <Owner-ModName>"
);
}
#[test]
fn advice_omits_empty_sections_without_leaving_blank_lines() {
assert_eq!(
advice_message("just a headline.", "", &[]),
"just a headline."
);
assert_eq!(
advice_message("headline.", "", &["vmm list"]),
"headline.\n\n vmm list"
);
assert_eq!(
advice_message("headline.", "detail.", &[]),
"headline.\n\ndetail."
);
}
#[test]
fn advice_indents_every_command_under_one_blank_line() {
assert_eq!(
advice_message("headline.", "detail.", &["vmm list", "vmm migrate"]),
"headline.\n\ndetail.\n\n vmm list\n vmm migrate"
);
}
#[test]
fn app_error_advice_displays_the_rendered_message() {
let error = AppError::advice("headline.", "detail.", &["vmm list"]);
assert_eq!(error.to_string(), "headline.\n\ndetail.\n\n vmm list");
}
}