ftp_cmd_list_parse/entry/
msdos.rs

1use std::convert::TryFrom;
2
3use ::regex::Regex;
4
5use super::*;
6
7lazy_static! {
8    static ref RELIST: Regex = Regex::new(
9        r"(?x)
10        ^(?P<month>\d{2})(?:\-|/)
11        (?P<date>\d{2})(?:\-|/)
12        (?P<year>\d{2,4})\s+
13        (?P<hour>\d{2}):(?P<minute>\d{2})\s{0,1}(?P<ampm>[AaMmPp]{1,2})\s+
14        (?:(?P<size>\d+)|(?P<isdir><DIR>))\s+
15        (?P<name>.+)$
16        "
17    )
18    .unwrap();
19}
20
21/// Represents entry from Msdos-like FTP server.
22#[derive(Debug)]
23pub struct FtpEntryMsdos {
24    kind: FtpEntryKind,
25    name: String,
26    size: usize,
27    // date: NaiveDateTime,
28    date_str: String,
29}
30
31impl FtpEntryMsdos {
32    /// Represents parsed string as entry of a MSDOS-like FTP server.
33    pub fn new(string: &str) -> Option<Self> {
34        FtpEntryMsdos::try_from(string).ok()
35    }
36}
37
38impl FtpEntryInfo for FtpEntryMsdos {
39    fn kind(&self) -> super::FtpEntryKind {
40        self.kind
41    }
42
43    fn name(&self) -> &str {
44        &self.name
45    }
46
47    fn size(&self) -> usize {
48        self.size
49    }
50
51    // fn date(&self) -> NaiveDateTime {
52    //     self.date
53    // }
54
55    fn date_str(&self) -> &str {
56        &self.date_str
57    }
58}
59
60impl TryFrom<&str> for FtpEntryMsdos {
61    type Error = ();
62
63    fn try_from(value: &str) -> Result<Self, Self::Error> {
64        if let Some(caps) = RELIST.captures(&value) {
65            let as_str = |s| caps.name(s).unwrap().as_str();
66
67            let name = as_str("name");
68            let kind = caps
69                .name("isdir")
70                .map_or(FtpEntryKind::File, |_| FtpEntryKind::Directory);
71            let size = caps
72                .name("size")
73                .map(|s| s.as_str().parse::<usize>())
74                .map_or(0, |r| r.map_or(0, |size| size));
75
76            let date_str = {
77                let month: u8 = as_str("month").parse().unwrap();
78                let date: u8 = as_str("date").parse().unwrap();
79                let year: u32 = match (as_str("year").len(), as_str("year").parse::<u32>().unwrap())
80                {
81                    (len, year) if len < 4 => year + if year < 70 { 2000 } else { 1900 },
82                    (_, year) => year,
83                };
84                let mut hour: u8 = as_str("hour").parse().unwrap();
85                let minute: u8 = as_str("minute").parse().unwrap();
86                if hour < 12
87                    && as_str("ampm")
88                        .bytes()
89                        .next()
90                        .unwrap()
91                        .eq_ignore_ascii_case(&b'p')
92                {
93                    hour += 12;
94                } else if hour == 12
95                    && as_str("ampm")
96                        .bytes()
97                        .next()
98                        .unwrap()
99                        .eq_ignore_ascii_case(&b'a')
100                {
101                    hour = 0;
102                }
103
104                format!(
105                    "{}-{:02}-{:02}T{:02}:{:02}",
106                    year, month, date, hour, minute
107                )
108            };
109
110            return Ok(Self {
111                name: name.to_owned(),
112                kind,
113                size,
114                date_str,
115            });
116        }
117
118        Err(())
119    }
120}