use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ActionMode {
Push,
Pull,
Link,
Out,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum InitError {
IoError { kind: std::io::ErrorKind },
AlreadyInitialized,
NotEmpty,
}
impl From<std::io::Error> for InitError {
fn from(err: std::io::Error) -> Self {
InitError::IoError { kind: err.kind() }
}
}
impl ToString for InitError {
fn to_string(&self) -> String {
match self {
InitError::IoError { kind: e_kind } => {
format!("IO error - {e_kind}")
}
InitError::AlreadyInitialized => {
String::from("This folder is already a Tendrils repo")
}
InitError::NotEmpty => {
String::from(
"This folder is not empty. Creating a Tendrils \
folder here may interfere with the existing \
contents.")
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum GetTendrilsRepoError {
GivenInvalid { path: PathBuf },
DefaultInvalid { path: PathBuf },
DefaultNotSet,
ConfigError(GetConfigError),
}
impl ToString for GetTendrilsRepoError {
fn to_string(&self) -> String {
match self {
GetTendrilsRepoError::GivenInvalid { path } => {
format!("{} is not a Tendrils repo", path.to_string_lossy())
}
GetTendrilsRepoError::DefaultInvalid { path } => {
format!("The default path \"{}\" is not a Tendrils repo", path.to_string_lossy())
}
GetTendrilsRepoError::DefaultNotSet => {
String::from("The default Tendrils repo path is not set")
}
GetTendrilsRepoError::ConfigError(err) => err.to_string(),
}
}
}
impl From<GetConfigError> for GetTendrilsRepoError {
fn from(err: GetConfigError) -> Self {
GetTendrilsRepoError::ConfigError(err.with_cfg_type(ConfigType::Global))
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum GetConfigError {
IoError { cfg_type: ConfigType, kind: std::io::ErrorKind },
ParseError { cfg_type: ConfigType, msg: String },
}
impl GetConfigError {
pub fn with_cfg_type(self, cfg_type: ConfigType) -> GetConfigError {
match self {
GetConfigError::IoError { kind, .. } => {
GetConfigError::IoError { cfg_type, kind }
}
GetConfigError::ParseError { msg, .. } => {
GetConfigError::ParseError { cfg_type, msg }
}
}
}
}
impl ToString for GetConfigError {
fn to_string(&self) -> String {
match self {
GetConfigError::IoError { cfg_type, kind } => format!(
"IO error while reading the {} file:\n{kind}",
cfg_type.file_name(),
),
GetConfigError::ParseError { cfg_type, msg } => format!(
"Could not parse the {} file:\n{msg}",
cfg_type.file_name(),
),
}
}
}
impl From<std::io::Error> for GetConfigError {
fn from(err: std::io::Error) -> Self {
GetConfigError::IoError { cfg_type: ConfigType::Repo, kind: err.kind() }
}
}
impl From<serde_json::Error> for GetConfigError {
fn from(err: serde_json::Error) -> Self {
GetConfigError::ParseError {
cfg_type: ConfigType::Repo,
msg: err.to_string()
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ConfigType {
Repo,
Global,
}
impl ConfigType {
fn file_name(&self) -> &str {
match self {
ConfigType::Repo => "tendrils.json",
ConfigType::Global => "global-config.json"
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SetupError {
CannotSymlink,
ConfigError(GetConfigError),
NoValidTendrilsRepo(GetTendrilsRepoError),
}
impl ToString for SetupError {
fn to_string(&self) -> String {
match self {
SetupError::CannotSymlink => String::from(
"Missing the permissions required to create symlinks on \
Windows. Consider:\n \
- Running this command in an elevated terminal\n \
- Enabling developer mode (this allows creating symlinks \
without requiring administrator priviledges)\n \
- Changing these tendrils to non-link modes instead"
),
SetupError::ConfigError(err) => err.to_string(),
SetupError::NoValidTendrilsRepo(err) => err.to_string(),
}
}
}
impl From<GetTendrilsRepoError> for SetupError {
fn from(err: GetTendrilsRepoError) -> Self {
SetupError::NoValidTendrilsRepo(err)
}
}
impl From<GetConfigError> for SetupError {
fn from(err: GetConfigError) -> Self {
SetupError::ConfigError(err)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TendrilActionSuccess {
New,
Overwrite,
NewSkipped,
OverwriteSkipped,
}
impl ToString for TendrilActionSuccess {
fn to_string(&self) -> String {
match self {
TendrilActionSuccess::New => String::from("Created"),
TendrilActionSuccess::NewSkipped => String::from("Skipped creation"),
TendrilActionSuccess::Overwrite => String::from("Overwritten"),
TendrilActionSuccess::OverwriteSkipped => {
String::from("Skipped overwrite")
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TendrilActionError {
IoError {
kind: std::io::ErrorKind,
loc: Location,
},
ModeMismatch,
TypeMismatch {
mistype: FsoType,
loc: Location,
},
}
impl From<std::io::Error> for TendrilActionError {
fn from(err: std::io::Error) -> Self {
TendrilActionError::IoError { kind: err.kind(), loc: Location::Unknown }
}
}
impl From<std::io::ErrorKind> for TendrilActionError {
fn from(e_kind: std::io::ErrorKind) -> Self {
TendrilActionError::IoError { kind: e_kind, loc: Location::Unknown }
}
}
impl ToString for TendrilActionError {
fn to_string(&self) -> String {
use std::io::ErrorKind::NotFound;
use FsoType::{Dir, File, SymDir, SymFile, BrokenSym};
use Location::{Dest, Source, Unknown};
match self {
TendrilActionError::IoError { kind: NotFound, loc: Source } => {
String::from("Source not found")
}
TendrilActionError::IoError { kind: NotFound, loc: Dest } => {
String::from("Destination not found")
}
TendrilActionError::IoError { kind: NotFound, loc: Unknown } => {
String::from("Not found")
}
TendrilActionError::IoError { kind: e_kind, loc: Source } => {
format!("{:?} error at source", e_kind)
}
TendrilActionError::IoError { kind: e_kind, loc: Dest } => {
format!("{:?} error at destination", e_kind)
}
TendrilActionError::IoError { kind: e_kind, loc: Unknown } => {
format!("{:?} error", e_kind)
}
TendrilActionError::ModeMismatch => {
String::from("Wrong tendril type")
}
TendrilActionError::TypeMismatch { loc: Source, mistype: File } => {
String::from("Unexpected file at source")
}
TendrilActionError::TypeMismatch { loc: Source, mistype: Dir } => {
String::from("Unexpected directory at source")
}
TendrilActionError::TypeMismatch {
loc: Source,
mistype: SymFile | SymDir | BrokenSym,
} => String::from("Unexpected symlink at source"),
TendrilActionError::TypeMismatch { loc: Dest, mistype: File } => {
String::from("Unexpected file at destination")
}
TendrilActionError::TypeMismatch { loc: Dest, mistype: Dir } => {
String::from("Unexpected directory at destination")
}
TendrilActionError::TypeMismatch {
loc: Dest,
mistype: SymFile | SymDir | BrokenSym,
} => String::from("Unexpected symlink at destination"),
TendrilActionError::TypeMismatch { loc: Unknown, mistype: _ } => {
String::from("Unexpected file system object")
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Location {
Source,
Dest,
Unknown,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum FsoType {
File,
Dir,
SymFile,
SymDir,
BrokenSym,
}
impl FsoType {
pub fn is_file(&self) -> bool {
self == &FsoType::File || self == &FsoType::SymFile
}
pub fn is_dir(&self) -> bool {
self == &FsoType::Dir || self == &FsoType::SymDir
}
pub fn is_symlink(&self) -> bool {
self == &FsoType::SymFile
|| self == &FsoType::SymDir
|| self == &FsoType::BrokenSym
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TendrilMode {
DirMerge,
DirOverwrite,
Link,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum InvalidTendrilError {
InvalidLocal,
Recursion,
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(untagged)]
pub(crate) enum OneOrMany<T> {
One(T),
Vec(Vec<T>),
}
impl<T> From<OneOrMany<T>> for Vec<T> {
fn from(from: OneOrMany<T>) -> Self {
match from {
OneOrMany::One(val) => vec![val],
OneOrMany::Vec(vec) => vec,
}
}
}
impl<T> From<Vec<T>> for OneOrMany<T> {
fn from(mut from: Vec<T>) -> Self {
match from.len() {
1 => OneOrMany::One(from.remove(0)),
_ => OneOrMany::Vec(from),
}
}
}