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}