stitchy_core/files/
path.rs

1use crate::{FileLocation, FileProperties};
2use image::{metadata::Orientation, DynamicImage, ImageFormat};
3
4use std::ffi::OsStr;
5use std::fs::File;
6use std::io::BufReader;
7use std::path::{Path, PathBuf};
8use std::time::SystemTime;
9
10/// Wrapper for file metadata.
11/// Loading these properties for a file does not require actually reading, let alone parsing, the
12/// file's contents.
13#[derive(Debug)]
14pub struct FilePathWithMetadata {
15    full_path: String,
16    modify_time: SystemTime,
17    size_bytes: u64,
18}
19
20impl Default for FilePathWithMetadata {
21    fn default() -> Self {
22        Self {
23            full_path: "".to_owned(),
24            modify_time: SystemTime::now(),
25            size_bytes: 0,
26        }
27    }
28}
29
30impl FileProperties for FilePathWithMetadata {
31    fn infer_format(&self) -> Option<ImageFormat> {
32        match self.full_path.rfind('.') {
33            Some(pos) => {
34                let extension = &self.full_path[pos..self.full_path.len()];
35                for &(ext, fmt) in crate::files::util::extension_formats().iter() {
36                    if ext == extension {
37                        return Some(fmt);
38                    }
39                }
40                None
41            }
42            None => None,
43        }
44    }
45
46    fn into_image_contents(self, print_info: bool) -> Result<DynamicImage, String> {
47        let path = Path::new(&self.full_path);
48        let image = image::open(path).map_err(|e| format!("Failed to open {:?}: {:?}", path, e))?;
49
50        if print_info {
51            if let Some(file_name) = path.file_name() {
52                let w = image.width();
53                let h = image.height();
54                println!(
55                    "Path: {}, w: {}, h: {}, {}",
56                    file_name.to_str().unwrap(),
57                    w,
58                    h,
59                    crate::util::make_size_string(self.size_bytes)
60                );
61            }
62        }
63
64        Ok(image)
65    }
66
67    #[inline]
68    fn file_size(&self) -> u64 {
69        self.size_bytes
70    }
71
72    #[inline]
73    fn modify_time(&self) -> SystemTime {
74        self.modify_time
75    }
76
77    #[inline]
78    fn full_path(&self) -> Option<&String> {
79        Some(&self.full_path)
80    }
81
82    fn orientation(&self) -> Result<Orientation, String> {
83        let file = File::open(&self.full_path)
84            .map_err(|e| format!("Cannot open file {}: {:?}", &self.full_path, e))?;
85        let reader = BufReader::new(file);
86        self.decode_orientation(reader)
87    }
88}
89
90/// Wrapper for a file's location by its absolute filesystem path
91#[derive(Default, Debug)]
92pub struct FilePath {
93    path: PathBuf,
94}
95
96impl FilePath {
97    pub fn new(path: PathBuf) -> Self {
98        Self { path }
99    }
100}
101
102impl FileLocation<FilePathWithMetadata> for FilePath {
103    fn is_file(&self) -> Result<bool, String> {
104        let path_buf = PathBuf::from(&self.path);
105        Ok(path_buf.is_file())
106    }
107
108    fn extension(&self) -> Result<String, String> {
109        let path_buf = PathBuf::from(&self.path);
110        path_buf
111            .extension()
112            .unwrap_or(OsStr::new(""))
113            .to_ascii_lowercase()
114            .into_string()
115            .map_err(|o| format!("Error reading file extension, found {:?}", o))
116    }
117
118    fn into_properties(self) -> Result<FilePathWithMetadata, String> {
119        // Get file size and modify date from its metadata
120        let metadata = self
121            .path
122            .metadata()
123            .map_err(|_| format!("Failed reading metadata for: {:?}", self.path))?;
124        let modify_time = metadata
125            .modified()
126            .map_err(|_| format!("Failed reading modify date for: {:?}", self.path))?;
127        let size_bytes = metadata.len();
128
129        // All seems well, get this file's properties
130        let path_string = match self.path.to_str() {
131            Some(s) => s.to_owned(),
132            None => return Err(format!("Failed converting to string: {:?}", self.path)),
133        };
134        let properties = FilePathWithMetadata {
135            full_path: path_string,
136            modify_time,
137            size_bytes,
138        };
139        Ok(properties)
140    }
141}