fb2_clean/config/
input.rs1use crate::Result;
2use std::{fs, path::Path};
3
4#[derive(Clone, Debug, PartialEq)]
6pub enum Input {
7 Dir(Box<Path>),
8 File(InputFile),
9}
10
11#[derive(Clone, Debug, Hash, Eq, PartialEq)]
13pub struct InputFile {
14 pub ty: InputFileType,
15 pub path: Box<Path>,
16}
17
18#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
20#[non_exhaustive]
21pub enum InputFileType {
22 Fb2,
23 Fb2Zip,
24}
25
26impl Input {
27 pub fn iter(&self) -> Box<dyn Iterator<Item = InputFile>> {
30 match self {
31 Self::Dir(d) => Box::new(
32 fs::read_dir(d)
33 .ok()
34 .into_iter()
35 .flat_map(|rd| rd.filter_map(std::result::Result::ok))
36 .filter_map(|entry| {
37 let path = entry.path();
38 get_input_file_type(&path).map(|ty| InputFile {
39 ty,
40 path: path.into(),
41 })
42 }),
43 ),
44 Self::File(f) => Box::new(Some(f.clone()).into_iter()),
45 }
46 }
47}
48
49impl Input {
50 pub(crate) fn new(path: impl AsRef<Path>) -> Result<Input> {
51 let path = fs::canonicalize(path)?.into_boxed_path();
52
53 if path.is_dir() {
54 Ok(Self::Dir(path))
55 } else if let Some(ty) = get_input_file_type(&path) {
56 Ok(Self::File(InputFile { ty, path }))
57 } else {
58 Err("File has unsupported extension".into())
59 }
60 }
61}
62
63impl InputFileType {
64 pub(crate) fn is_fb2(&self) -> bool {
65 matches!(self, Self::Fb2)
66 }
67
68 pub(crate) fn is_fb2_zip(&self) -> bool {
69 matches!(self, Self::Fb2Zip)
70 }
71}
72
73fn get_input_file_type(f: &Path) -> Option<InputFileType> {
74 let bytes = f.as_os_str().as_encoded_bytes();
75 let len = bytes.len();
76
77 for (ext, ty) in [
78 (".fb2", InputFileType::Fb2),
79 (".fb2.zip", InputFileType::Fb2Zip),
80 ] {
81 let ext = ext.as_bytes();
82 let l = ext.len();
83
84 if l > len {
85 break;
86 }
87 if bytes[len - l..len].eq_ignore_ascii_case(ext) {
88 return Some(ty);
89 }
90 }
91
92 None
93}