1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use assert_fs::{
    fixture::{ChildPath, FixtureError, PathChild},
    TempDir,
};
use std::{
    fmt,
    path::{Path, PathBuf},
};
use thiserror::Error;

pub enum TestingDirectory {
    Temp(TempDir),
    User(PathBuf),
}

impl fmt::Debug for TestingDirectory {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Temp(temp_dir) => write!(f, "{}", temp_dir.path().to_string_lossy()),
            Self::User(path) => write!(f, "{}", path.to_string_lossy()),
        }
    }
}

impl TestingDirectory {
    #[allow(dead_code)]
    pub fn path(&self) -> &Path {
        match self {
            TestingDirectory::User(path_buf) => path_buf,
            TestingDirectory::Temp(temp_dir) => temp_dir.path(),
        }
    }

    pub fn new_temp() -> Result<Self, FixtureError> {
        Ok(Self::Temp(TempDir::new()?))
    }

    pub fn from_temp(temp_dir: TempDir) -> Self {
        Self::Temp(temp_dir)
    }

    pub fn into_persistent(self) -> Self {
        if let Self::Temp(temp_dir) = self {
            return Self::Temp(temp_dir.into_persistent());
        }
        self
    }
}

impl TryInto<TempDir> for TestingDirectory {
    type Error = Error;

    fn try_into(self) -> Result<TempDir, Self::Error> {
        match self {
            TestingDirectory::Temp(temp) => Ok(temp),
            TestingDirectory::User(_) => Err(Error::CannotConvertToTempDir),
        }
    }
}

impl PathChild for TestingDirectory {
    fn child<P>(&self, path: P) -> ChildPath
    where
        P: AsRef<Path>,
    {
        match self {
            Self::User(dir_path) => ChildPath::new(dir_path.join(path)),
            Self::Temp(temp_dir) => temp_dir.child(path),
        }
    }
}

impl From<PathBuf> for TestingDirectory {
    fn from(path: PathBuf) -> Self {
        Self::User(path)
    }
}

impl From<Option<PathBuf>> for TestingDirectory {
    fn from(maybe_path: Option<PathBuf>) -> Self {
        if let Some(testing_directory) = maybe_path {
            testing_directory.into()
        } else {
            Default::default()
        }
    }
}

impl Clone for TestingDirectory {
    fn clone(&self) -> Self {
        match self {
            Self::User(dir_path) => dir_path.to_path_buf().into(),
            Self::Temp(_) => Default::default(),
        }
    }
}

impl Default for TestingDirectory {
    fn default() -> Self {
        Self::new_temp().unwrap()
    }
}

use serde::{Deserialize, Deserializer, Serialize, Serializer};

impl Serialize for TestingDirectory {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        self.path().serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for TestingDirectory {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let path: PathBuf = PathBuf::deserialize(deserializer).unwrap();
        Ok(path.into())
    }
}

#[derive(Error, Debug)]
pub enum Error {
    #[error(transparent)]
    Fixture(#[from] FixtureError),
    #[error("cannot convert to temp dir us user directory is used")]
    CannotConvertToTempDir,
}