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
use ascii_table::AsciiTable;
use mime::Mime;
use url::Url;

use crate::{common::MimeType, Error, ErrorKind, Result};
use std::{
    convert::TryFrom,
    fmt::{Display, Formatter},
    path::PathBuf,
    str::FromStr,
};

pub enum UserPath {
    Url(Url),
    File(PathBuf),
}

impl UserPath {
    pub fn get_mime(&self) -> Result<Mime> {
        Ok(match self {
            Self::Url(url) => Ok(url.into()),
            Self::File(f) => MimeType::try_from(f.as_path()),
        }?
        .0)
    }
}

impl FromStr for UserPath {
    type Err = Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let normalized = match url::Url::parse(s) {
            Ok(url) if url.scheme() == "file" => {
                let path = url.to_file_path().map_err(|_| {
                    Error::from(ErrorKind::BadPath(url.path().to_owned()))
                })?;

                Self::File(path)
            }
            Ok(url) => Self::Url(url),
            _ => Self::File(PathBuf::from(s)),
        };

        Ok(normalized)
    }
}

impl Display for UserPath {
    fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
        match self {
            Self::File(f) => fmt.write_str(&f.to_string_lossy()),
            Self::Url(u) => fmt.write_str(u.as_ref()),
        }
    }
}

pub fn mime_table(paths: &[UserPath], output_json: bool) -> Result<(), Error> {
    if output_json {
        let rows = paths
            .iter()
            .map(|path| {
                Ok(json::object! {
                    path: path.to_string(),
                    mime: path.get_mime()?.essence_str().to_owned()
                })
            })
            .collect::<Result<json::Array>>()?;
        println!("{}", json::stringify(rows));
    } else {
        let rows = paths
            .iter()
            .map(|path| {
                Ok(vec![
                    path.to_string(),
                    path.get_mime()?.essence_str().to_owned(),
                ])
            })
            .collect::<Result<Vec<Vec<String>>>>()?;

        let table = AsciiTable::default();
        table.print(rows);
    }

    Ok(())
}