use std::result; use std::path::PathBuf; use std::io; use thiserror::Error; use chrono::NaiveDateTime; use itertools::Itertools; fn format_paths(paths: &[PathBuf]) -> String { paths .iter() .map(|p| format!(" - {}", p.display())) .collect_vec() .join("\n") } #[derive(Debug, Error)] pub enum Error { #[error("The subcommand '{0}' is not implemented")] UnimplementedCommand(String), /// Sometimes a specific variant for an error is not necessary if the error /// can only happen in one place in the code. This is what the generic error /// is for and nothing else. #[error("{0}")] GenericFailure(String), #[error("Sqlite error: {0}")] Sqlite(#[from] rusqlite::Error), #[error("Could not find home directory or a place where configuration can be read an written :(")] NoHomeDir, #[error("Could not read the config file at {path} with error: {error}")] CouldntReadConfigFile { path: PathBuf, error: io::Error, }, #[error("Couldn't parse toml file at: {path}\nwith error: {error}")] TomlError{ path: PathBuf, error: toml::de::Error}, #[error("Couldn't parse yaml file at: {path}\nwith error: {error}")] YamlError{ path: PathBuf, error: serde_yaml::Error}, #[error("Could not understand '{0}' as a date format. Some options are: Something similar to ISO format will be parsed as a time in the computer's timezone. * '2021-01-13' a date * '2019-05-03 11:13' a date with portions of a time ISO format with offset or UTC will be parsed as a time in the specified timezone. Use 'Z' for 'UTC' and an offset for everything else * '2021-01-13Z' * '2005-10-14 19:20:35+05:00' something that looks like an hour will be parsed as a time in the current day in the computer's timezone. Add 'Z' or an offset to specify the timezone. * '11:30' * '23:50:45' (with seconds) some human times, for now restricted to time ago: * 'an hour ago' * 'a minute ago' * '50 min ago' * '1h30m ago' * 'two hours thirty minutes ago'")] DateTimeParseError(String), #[error("IOError: {0}")] IOError(#[from] std::io::Error), #[error("CSV Error: {0}")] CSVError(#[from] csv::Error), #[error("Could not serialize to json. {0}")] SerdeJsonError(#[from] serde_json::Error), #[error("Corrupted data found in the database: {0} To fix this first find where your database is located using t configure and query it using t backend or the sqlite3 command provided by your system.")] CorruptedData(String), #[error("Trying to parse {0} as a time in your timezone led to no results")] NoneLocalTime(String), #[error("Trying to parse {orig} as a time in your timezone led to the ambiguous results {t1} and {t2}")] AmbiguousLocalTime { orig: String, t1: NaiveDateTime, t2: NaiveDateTime, }, #[error("The provided regex '{0}' could not be parsed")] InvalidRegex(String), #[error("Could not understand '{0}' as a month specifier. Try 'this', 'last', or any month name like 'january' or 'nov'")] InvalidMonthSpec(String), #[error("Could not understand '{0}' as a week day. Try 'monday' or 'TuesDay'")] InvalidWeekDaySpec(String), #[error("Could not understand '{0}' as a number of hours")] InvalidHours(String), #[error("An error ocurred while trying to read entries from the database. In the row with id {id} the data at column '{col}' has a value that is not a valid time. A valid time looks like this: '2021-07-12 19:35:43.645916' and must be in UTC timezone (unless you are using a database that was created by timetrap, case in which the times are in the local timezone). To fix this problem you can use t backend and manually update the value using good old SQL.")] InvalidTimeInDatabase { id: String, col: String, }, #[error("A note wasn't provided, and the config file specifies that the note is required, but an empty string was found in the config value for 'note_editor'. You can fix this by running t config and specifying a note editor. Check t config --help for more options.")] EditorIsEmpty, #[error("Running editor '{editor}' failed with error: '{error}'")] EditorFailed { editor: String, error: String, }, #[error("A temporary file couldn't be created for you to edit. This is very weird, but might have something to do with yout OS. Here's the underlaying error: {0} If you think this is an issue with this program report it at: https://gitlab.com/categulario/tiempo")] NoTmpFile(String), #[error("The temporary file you just created couldn't be read. This is the underlaying error: {0} I didn't imagine this could ever happen, but now I'm curious. Do you mind reporging the issue at https://gitlab.com/categulario/tiempo ?")] CouldntReadTmpFile(String), #[error("The ")] CouldntCreateConfigDir { path: PathBuf, error: String, }, #[error("A problem ocurred while trying to edit/create the config file at {path} Is there a permissions issue? Here's the underlaying error: {error}")] CouldntEditConfigFile { path: PathBuf, error: String, }, #[error("Trying to run system command sqlite3 failed with error: {0}")] Sqlite3CommandFailed(std::io::Error), #[error("Trying to run system command sqlite3 failed")] Sqlite3CommandFailedUnkown, #[error("The specified name for a custom formatter \"{0}\" is not valid. Only ascii letters, numbers, dots, dashes and underscores are allowed.")] InvalidCustomFormatter(String), #[error("You have specified a custom formatter \"{0}\" but your config file doesn't say where to look for it. You can set a path using t config --formatter-search-paths ..")] NoFormatterSearchPaths(String), #[error("Could not find a formatter with name '{name}' in any of the following paths: {0} which where taken from your config file located at {config_at} Perhaps it is mispelled?", format_paths(.paths))] FormatterNotFound { name: String, paths: Vec, config_at: PathBuf, }, #[error("The custom formatter located at: {0} failed with error: {1}")] CustomFormatterFailed(PathBuf, std::io::Error), } pub type Result = result::Result;