ftp_cmd_list_parse/
entry.rs

1mod msdos;
2mod unix;
3
4use std::{
5    convert::{TryFrom, TryInto},
6    fmt::Display,
7    ops::Deref,
8};
9
10pub use msdos::FtpEntryMsdos;
11pub use unix::FtpEntryUnix;
12
13/// Permissions of the Unix-like entry.
14#[non_exhaustive]
15#[derive(Debug)]
16pub struct FtpEntryPermissions(String);
17
18impl FtpEntryPermissions {
19    pub fn as_str(&self) -> &str {
20        &self.0
21    }
22}
23
24impl Display for FtpEntryPermissions {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        write!(f, "{}", &self.0)
27    }
28}
29
30/// Type of the ftp entry.
31#[derive(Debug, Copy, Clone, PartialEq)]
32pub enum FtpEntryKind {
33    UNKNOWN,
34    Directory,
35    File,
36    BlockDevice,
37    CharacterDevice,
38    Pipe,
39    Socket,
40    Symlink,
41    // Symlink(FtpEntryPath)
42}
43
44impl From<char> for FtpEntryKind {
45    fn from(value: char) -> Self {
46        match value {
47            '-' => Self::File,
48            'd' => Self::Directory,
49            'b' => Self::BlockDevice,
50            'c' => Self::CharacterDevice,
51            'p' => Self::Pipe,
52            's' => Self::Socket,
53            'l' => Self::Symlink,
54            _ => Self::UNKNOWN,
55        }
56    }
57}
58
59impl TryFrom<&str> for FtpEntryKind {
60    type Error = &'static str;
61
62    fn try_from(value: &str) -> Result<Self, Self::Error> {
63        if value.len() == 1 {
64            Ok(value.chars().nth(0).unwrap().into())
65        } else {
66            Err("length of the value must be equal to 1")
67        }
68    }
69}
70
71/// All fields that supports both servers: Unix & MSDOS
72pub trait FtpEntryInfo {
73    /// Returns a new [`FtpEntry`] by given string if parsing was successful.
74    /// Also you can create new [`FtpEntry`] by use [`TryFrom`] trait.
75    /// ```rust
76    /// # use ftp_cmd_list_parse::FtpEntry;
77    /// let ftp_response = "drwxr-xr-x  10 root   root    4096 Dec 21  2012 usr";
78    ///
79    /// match FtpEntry::new(ftp_response) {
80    ///     Some(ftp_entry) => {
81    ///         assert_eq!(ftp_entry.name(), "usr");
82    ///         assert_eq!(ftp_entry.size(), 4096);
83    ///         assert_eq!(ftp_entry.date_str(), "Dec 21 2012");
84    ///     }
85    ///     None => println!("ftp_response is not valid ftp-entry!")
86    /// }
87    /// ```
88    fn new(string: &str) -> Option<Self>
89    where
90        for<'a> Self: TryFrom<&'a str>,
91    {
92        Self::try_from(string).ok()
93    }
94
95    /// Represents type of the entry: directory, file, symlink or other.
96    fn kind(&self) -> FtpEntryKind;
97    /// Returns name of the entry.
98    fn name(&self) -> &str;
99    /// Returns size of the entry.
100    fn size(&self) -> usize;
101    // fn date(&self) -> NaiveDateTime;
102    /// Returns date of the entry.
103    fn date_str(&self) -> &str;
104}
105
106/// Represents parsed string as ftp entry.
107///
108/// Implements [`Deref`] to `&dyn FtpEntryInfo`, so you can get access
109/// to general fields that supports both servers: Unix & MSDOS.
110#[derive(Debug)]
111pub enum FtpEntry {
112    Unix(FtpEntryUnix),
113    Msdos(FtpEntryMsdos),
114}
115
116impl FtpEntry {
117    /// Returns a new [`FtpEntry`] by given string if parsing was successful.
118    /// Also you can create new [`FtpEntry`] by use [`TryFrom`] or [`TryInto`] traits.
119    /// ```rust
120    /// # use ftp_cmd_list_parse::FtpEntry;
121    /// let ftp_response = "drwxr-xr-x  10 root   root    4096 Dec 21  2012 usr";
122    ///
123    /// match FtpEntry::new(ftp_response) {
124    ///     Some(ftp_entry) => {
125    ///         assert_eq!(ftp_entry.name(), "usr");
126    ///         assert_eq!(ftp_entry.size(), 4096);
127    ///         assert_eq!(ftp_entry.date_str(), "Dec 21 2012");
128    ///     }
129    ///     None => println!("ftp_response is not valid ftp-entry!")
130    /// }
131    /// ```
132    pub fn new(string: &str) -> Option<Self> {
133        string.try_into().ok()
134    }
135
136    /// Returns true if [`FtpEntry`] has UNIX-like entry, otherwise false.
137    pub fn is_unix_type(&self) -> bool {
138        match self {
139            FtpEntry::Unix(_) => true,
140            _ => false,
141        }
142    }
143
144    /// Returns true if [`FtpEntry`] has MSDOS-like entry, otherwise false.
145    pub fn is_msdos_type(&self) -> bool {
146        match self {
147            FtpEntry::Msdos(_) => true,
148            _ => false,
149        }
150    }
151
152    /// Converts [`FtpEntry`] to [`FtpEntryUnix`].
153    /// Its may be useful if you need to get additional infomation
154    /// like permissions, group, owner and others.
155    ///
156    /// # Panics
157    ///
158    /// Panics if the value is not a Unix-like entry.
159    /// If you not sure what kind of [`FtpEntry`] is, use [`try_to_unix_type`](#method.try_to_unix_type) instead.
160    pub fn to_unix_type(self) -> FtpEntryUnix {
161        self.try_to_unix_type().expect("FtpEntryType missmatch")
162    }
163
164    /// Converts [`FtpEntry`] to [`FtpEntryMsdos`].
165    ///
166    /// # Panics
167    ///
168    /// Panics if the value is not a Msdos-like entry.
169    /// If you not sure what kind of [`FtpEntry`] is, use [`try_to_msdos_type`](#method.try_to_msdos_type) instead.
170    pub fn to_msdos_type(self) -> FtpEntryMsdos {
171        self.try_to_msdos_type().expect("FtpEntryType missmatch")
172    }
173
174    /// Tries to convert [`FtpEntry`] to [`FtpEntryUnix`].
175    /// If it is impossible, returns [`FtpEntry`] back to caller.
176    pub fn try_to_unix_type(self) -> Result<FtpEntryUnix, Self> {
177        if let FtpEntry::Unix(entry) = self {
178            Ok(entry)
179        } else {
180            Err(self)
181        }
182    }
183
184    /// Tries to convert [`FtpEntry`] to [`FtpEntryMsdos`].
185    /// If it is impossible, returns [`FtpEntry`] back to caller.
186    pub fn try_to_msdos_type(self) -> Result<FtpEntryMsdos, Self> {
187        if let FtpEntry::Msdos(entry) = self {
188            Ok(entry)
189        } else {
190            Err(self)
191        }
192    }
193}
194
195impl Deref for FtpEntry {
196    type Target = dyn FtpEntryInfo;
197
198    fn deref(&self) -> &Self::Target {
199        match self {
200            FtpEntry::Msdos(entry) => entry,
201            FtpEntry::Unix(entry) => entry,
202        }
203    }
204}
205
206impl TryFrom<&str> for FtpEntry {
207    type Error = ();
208
209    fn try_from(value: &str) -> Result<Self, Self::Error> {
210        if let Ok(entry) = FtpEntryUnix::try_from(value) {
211            return Ok(FtpEntry::Unix(entry));
212        }
213
214        if let Ok(entry) = FtpEntryMsdos::try_from(value) {
215            return Ok(FtpEntry::Msdos(entry));
216        }
217
218        Err(())
219    }
220}