use std::{
fmt::Display,
io::{Error as IoError, ErrorKind},
path::Path,
};
use colored::Colorize;
#[doc(hidden)]
trait MessageType {
const PREFIX: &'static str;
const TO_STDERR: bool = false;
}
#[doc(hidden)]
struct Error;
impl MessageType for Error {
const PREFIX: &'static str = "ERROR";
const TO_STDERR: bool = true;
}
fn format_message<T: MessageType>(title: &str, details: &str) -> String {
format!("{}: {title}\n\n{details}", T::PREFIX.red().bold())
}
fn format_message_with_suggestion<T: MessageType>(
title: &str,
details: &str,
suggestion: &str,
) -> String {
format!("{}\n\n{suggestion}", format_message::<T>(title, details))
}
fn print_message_with_suggestion<T: MessageType>(title: &str, details: &str, suggestion: &str) {
let message = format_message_with_suggestion::<T>(title, details, suggestion);
if T::TO_STDERR {
eprintln!("{message}");
} else {
println!("{message}");
}
}
pub fn print_error(title: &str, details: &str, suggestion: &str) {
print_message_with_suggestion::<Error>(title, details, suggestion);
}
pub fn format_list<T: Display>(items: &[T]) -> String {
items
.iter()
.map(|item| format!(" - {item}"))
.collect::<Vec<_>>()
.join("\n")
}
pub fn check_for_file_in_folder(file_path: &Path, folder_path: &Path) -> Result<bool, IoError> {
if file_path.as_os_str().is_empty() {
return Err(IoError::new(ErrorKind::InvalidInput, "File path is empty"));
}
if folder_path.as_os_str().is_empty() {
return Err(IoError::new(
ErrorKind::InvalidInput,
"Folder path is empty",
));
}
let file_parent = file_path.parent().ok_or_else(|| {
IoError::new(
ErrorKind::InvalidInput,
"Invalid file path: cannot get parent directory",
)
})?;
Ok(file_parent.starts_with(folder_path))
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::Path;
#[test]
fn test_check_for_file_in_folder() -> std::result::Result<(), Box<dyn std::error::Error>> {
assert!(check_for_file_in_folder(
Path::new("src/file.rs"),
Path::new("src")
)?);
assert!(check_for_file_in_folder(
Path::new("src/nested/deep/file.rs"),
Path::new("src")
)?);
assert!(!check_for_file_in_folder(
Path::new("other/file.rs"),
Path::new("src")
)?);
Ok(())
}
#[test]
fn test_check_for_file_in_folder_errors() {
assert!(check_for_file_in_folder(Path::new(""), Path::new("src")).is_err());
assert!(check_for_file_in_folder(Path::new("file.txt"), Path::new("")).is_err());
}
#[test]
fn test_format_list() {
let items = vec!["item1", "item2", "item3"];
let formatted = format_list(&items);
assert_eq!(formatted, " - item1\n - item2\n - item3");
let empty: Vec<&str> = vec![];
assert_eq!(format_list(&empty), "");
let single = vec!["item"];
assert_eq!(format_list(&single), " - item");
}
}