use super::util::scrub_path;
use camino::{FromPathBufError, Utf8Path};
use std::{ffi::OsStr, io, str::Utf8Error};
use thiserror::Error as ThisError;
#[derive(ThisError, Debug)]
#[error(transparent)]
pub struct IoErr(#[from] io::Error);
#[derive(ThisError, Debug)]
#[error(transparent)]
pub struct Utf8Err(#[from] Utf8Error);
#[derive(ThisError, Debug)]
pub enum FileError {
#[error(transparent)]
IoError(#[from] io::Error),
}
#[derive(ThisError, Debug)]
#[error(transparent)]
pub struct PathIoError(#[from] io::Error);
#[derive(ThisError, Debug)]
#[error("Malformed Path")]
pub struct PathConvertError {
#[source]
source: PathIoError,
}
impl PathConvertError {
pub fn new(source: FromPathBufError) -> Self {
let source = PathIoError(source.into_io_error());
Self { source }
}
}
#[derive(ThisError, Debug)]
pub enum HomeError {
#[error("Unable to convert the retrieved user home directory: '{path}'")]
RetrieveConvertError {
path: String,
source: PathConvertError,
},
#[error("Unable to retrieve the user home directory")]
RetrieveLocateError,
#[error("The home directory: '{0}' is not absolute")]
NotAbsolute(String),
}
impl HomeError {
pub fn retrieve_convert_error<T>(
path: &T,
source: PathConvertError,
) -> Self
where
T: AsRef<OsStr> + ?Sized,
{
let path = path.as_ref().to_string_lossy().to_string();
Self::RetrieveConvertError { path, source }
}
}
#[derive(ThisError, Debug)]
pub enum CwdError {
#[error("Unable to convert the retrieved user's current working directory: '{path}'")]
RetrieveConvertError {
path: String,
source: PathConvertError,
},
#[error(
"Unable to retrieve the user's current working directory"
)]
RetrieveLocateError(#[source] io::Error),
#[error("The current working directory: '{0}' is not absolute")]
NotAbsolute(String),
}
impl CwdError {
pub fn retrieve_convert_error<T>(
path: &T,
source: PathConvertError,
) -> Self
where
T: AsRef<OsStr> + ?Sized,
{
let path = scrub_path(&path, None);
Self::RetrieveConvertError { path, source }
}
}
#[derive(ThisError, Debug)]
pub enum ExpandPathLexerError {
#[error("Missing the '$' symbol at position {0}")]
MissingIdentifierSymbol(usize),
#[error("Invalid starting identifer character '{wide_char} at position {position} must be _[a-z][A-Z]")]
InvalidIdentifierStartCharacter {
wide_char: String,
position: usize,
},
#[error("Missing closing curly bracket '}}' for matching curly at position {0}")]
MissingClosingCurly(usize),
#[error("Missing the identifier name for the '$' symbol at position {0}")]
MissingIdentifierName(usize),
#[error("The tilde character '~' can only be followed by the path separator '{0}'")]
InvalidTildeUse(String),
}
impl ExpandPathLexerError {
pub fn invalid_identifier_start_character(
wide_char: &str,
position: usize,
) -> Self {
let wide_char = wide_char.to_owned();
Self::InvalidIdentifierStartCharacter {
wide_char,
position,
}
}
}
#[derive(ThisError, Debug)]
pub enum ExpandPathError {
#[error("Unable to expand: {path}")]
LexerError {
path: String,
#[source]
source: ExpandPathLexerError,
},
#[error("Unable to expand: '{path}' the identifier '{identifier}' is not set or empty")]
IdentifierExpandError { path: String, identifier: String },
#[error("The home directory is unknown.")]
UnknownHome,
#[error("The current working directory is unknown.")]
UnknownCwd,
#[error("Cannot expand empty value")]
EmptyPathError,
#[error("Unable to set the home directory to: '{0}' because it's not an absolute path")]
NotAbsoluteHome(String),
#[error(
"Unable to set the current working directory to: '{0}' because it's not an absolute path"
)]
NotAbsoluteCwd(String),
}
impl ExpandPathError {
pub fn lexer_error<T>(
path: &T,
source: ExpandPathLexerError,
) -> Self
where
T: AsRef<OsStr> + ?Sized,
{
let path = scrub_path(&path, None);
Self::LexerError { path, source }
}
pub fn identifier_expand_error<T>(
path: &T,
identifier: &str,
) -> Self
where
T: AsRef<OsStr> + ?Sized,
{
let path = scrub_path(&path, None);
let identifier = identifier.to_string();
Self::IdentifierExpandError { path, identifier }
}
}
#[derive(ThisError, Debug)]
pub enum IdentifierKeyError {
#[error("Non-ascii character")]
NonAscii { position: usize },
#[error("Must be alphanumeric or an underscore")]
NonAlphaNumericUnderscore { position: usize },
#[error("Missing identifier name")]
EmptyIdentifierName { position: usize },
#[error("Invalid identifier name")]
InvalidName { position: usize },
#[error(
"Missing starting curly '{{'; use double '$$' to escape"
)]
MissingStartingCurly { position: usize },
#[error("Missing closing curly '}}'")]
MissingClosingCurly { position: usize },
#[error("Identifier name cannot start with a number")]
StartsWithNumber { position: usize },
}
impl IdentifierKeyError {
pub fn non_ascii(position: usize) -> Self {
let position = position + 1;
Self::NonAscii { position }
}
pub fn non_alpha_numeric_underscore(position: usize) -> Self {
let position = position + 1;
Self::NonAlphaNumericUnderscore { position }
}
pub fn empty_identifier_name(position: usize) -> Self {
let position = position + 1;
Self::EmptyIdentifierName { position }
}
pub fn invalid_name(position: usize) -> Self {
let position = position + 1;
Self::InvalidName { position }
}
pub fn missing_starting_curly(position: usize) -> Self {
let position = position + 1;
Self::MissingStartingCurly { position }
}
pub fn missing_closing_curly(position: usize) -> Self {
let position = position + 1;
Self::MissingClosingCurly { position }
}
pub fn starts_with_number(position: usize) -> Self {
let position = position + 1;
Self::StartsWithNumber { position }
}
}
#[derive(ThisError, Debug)]
pub enum ExpandIdentifierError {
#[error("Unable to fetch the value for the identifier '{0}'")]
ValueError(String),
#[error("Identifier Error at line: {line_number} position: {position}")]
KeyError {
line_number: usize,
position: usize,
#[source]
source: IdentifierKeyError,
},
}
impl ExpandIdentifierError {
pub fn key_error(
line_number: usize,
source: IdentifierKeyError,
) -> Self {
let line_number = line_number + 1;
let position = match source {
IdentifierKeyError::NonAscii { position } => position,
IdentifierKeyError::NonAlphaNumericUnderscore {
position,
} => position,
IdentifierKeyError::EmptyIdentifierName { position } => {
position
},
IdentifierKeyError::InvalidName { position } => position,
IdentifierKeyError::MissingStartingCurly {
position,
} => position,
IdentifierKeyError::MissingClosingCurly { position } => {
position
},
IdentifierKeyError::StartsWithNumber { position } => {
position
},
};
Self::KeyError {
line_number,
position,
source,
}
}
pub fn value_error(name: &str) -> Self {
let name = name.to_string();
Self::ValueError(name)
}
}
#[derive(ThisError, Debug)]
pub enum ExpandTextError {
#[error("Error in file: '{path}'")]
IdentifierErrorInFile {
path: String,
#[source]
source: ExpandIdentifierError,
},
#[error("Unable to read file: '{path}'")]
ReadFileError {
path: String,
#[source]
source: IoErr,
},
#[error(transparent)]
IdentifierError(#[from] ExpandIdentifierError),
}
impl ExpandTextError {
pub fn identifier_error_in_file(
path: &Utf8Path,
source: ExpandIdentifierError,
) -> Self {
let path = scrub_path(&path, None);
Self::IdentifierErrorInFile { path, source }
}
pub fn read_file_error(
path: &Utf8Path,
source: std::io::Error,
) -> Self {
let path = scrub_path(&path, None);
let source = IoErr(source);
Self::ReadFileError { path, source }
}
}
#[derive(ThisError, Debug)]
pub enum KomichiError {
#[error("Unable to convert: '{path}'")]
PathConvertError {
path: String,
#[source]
source: PathConvertError,
},
#[error(transparent)]
HomeError(#[from] HomeError),
#[error(transparent)]
CwdError(#[from] CwdError),
#[error(transparent)]
ExpandPathError(#[from] ExpandPathError),
#[error(transparent)]
ExpandTextError(#[from] ExpandTextError),
}