1use std::fs::{self, read_dir, File, ReadDir};
2use std::ops::Deref;
3use std::path::{Path, PathBuf};
4
5use crate::flags::{self, Flags};
6use crate::{util, Error, M2dir};
7
8#[derive(Clone, Debug, PartialEq, Eq, Hash)]
13pub struct Message {
14 pub(crate) id: String,
15 pub(crate) path: PathBuf,
16}
17
18impl TryFrom<&Path> for Message {
19 type Error = Error;
20
21 fn try_from(path: &Path) -> Result<Self, Error> {
22 if !path.is_file() {
23 return Err(Error::MessageNotFound);
24 }
25 let fname = path.file_name().unwrap().to_string_lossy();
26 let (_, id) = fname
27 .rsplit_once(',')
28 .ok_or(Error::InvalidFileName(path.to_path_buf()))?;
29 Ok(Message {
30 id: id.to_string(),
31 path: PathBuf::from(path),
32 })
33 }
34}
35
36impl Message {
37 pub fn path(&self) -> &Path {
38 &self.path
39 }
40
41 pub fn id(&self) -> &str {
42 &self.id
43 }
44
45 pub fn flags_path(&self) -> PathBuf {
46 flags::flags_path_for(self.path.parent().unwrap(), self.id())
47 }
48
49 pub fn flags(&self) -> Result<Flags, Error> {
50 let flags_path = self.flags_path();
51
52 if !flags_path.exists() {
53 return Ok(Flags::default());
54 }
55
56 let file = File::open(&flags_path)?;
57 Flags::parse_file(file).map_err(|e| Error::Flags(flags_path, e))
58 }
59
60 pub fn set_flags(&self, flags: &Flags) -> Result<(), Error> {
61 let flags_path = self.flags_path();
62 fs::create_dir_all(flags_path.parent().unwrap())
63 .map_err(|e| Error::WriteFlags(flags_path.clone(), e))?;
64 let mut file =
65 File::create(&flags_path).map_err(|e| Error::WriteFlags(flags_path.clone(), e))?;
66 flags
67 .write_file(&mut file)
68 .map_err(|e| Error::WriteFlags(flags_path, e))?;
69 Ok(())
70 }
71
72 pub fn copy_to(&self, target: &M2dir) -> Result<Message, Error> {
73 let src_flags = self.flags_path();
74 let dst_flags = flags::flags_path_for(&target.path, self.id());
75 let dst = PathBuf::from_iter([target.path.as_os_str(), self.path.file_name().unwrap()]);
76
77 fs::create_dir_all(dst_flags.parent().unwrap())?;
78 if src_flags.exists() {
79 util::copy_atomic(src_flags, &dst_flags)?;
80 }
81 util::copy_atomic(&self.path, &dst).inspect_err(|_| {
82 _ = fs::remove_file(&dst_flags);
83 })?;
84 Ok(Message {
85 id: self.id.clone(),
86 path: dst,
87 })
88 }
89
90 pub fn move_to(&mut self, target: &M2dir) -> Result<(), Error> {
91 let src_flags = self.flags_path();
92 let dst_flags = flags::flags_path_for(&target.path, self.id());
93 let dst = PathBuf::from_iter([target.path.as_os_str(), self.path.file_name().unwrap()]);
94
95 fs::create_dir_all(dst_flags.parent().unwrap())?;
96 if src_flags.exists() {
97 fs::rename(&src_flags, &dst_flags)?;
98 }
99 fs::rename(&self.path, &dst).inspect_err(|_| {
100 _ = fs::rename(&dst_flags, &src_flags);
101 })?;
102 self.path = dst;
103 Ok(())
104 }
105
106 pub fn delete(self) -> Result<(), Error> {
107 let flags = self.flags_path();
108 fs::remove_file(self.path())?;
109 if flags.exists() {
110 fs::remove_file(self.flags_path())?;
111 }
112 Ok(())
113 }
114}
115
116pub struct Messages {
126 path: PathBuf,
127 readdir: Option<ReadDir>,
128}
129
130impl Messages {
131 pub(crate) fn new(path: PathBuf) -> Messages {
132 Messages {
133 path,
134 readdir: None,
135 }
136 }
137
138 pub fn path(&self) -> &Path {
139 &self.path
140 }
141}
142
143impl Iterator for Messages {
144 type Item = Result<Message, Error>;
145
146 fn next(&mut self) -> Option<Result<Message, Error>> {
147 if self.readdir.is_none() {
148 self.readdir = match read_dir(&self.path) {
149 Err(_) => return None,
150 Ok(v) => Some(v),
151 };
152 }
153
154 loop {
155 let dir_entry = self.readdir.iter_mut().next().unwrap().next();
157 let result = dir_entry.map(|e| {
158 let entry = e?;
159 let ftype = entry.file_type()?;
160 if ftype.is_dir() {
161 return Ok(None);
162 }
163 let filename = String::from(entry.file_name().to_string_lossy().deref());
164 if filename.starts_with('.') {
165 return Ok(None);
166 }
167 Ok(Some(Message::try_from(entry.path().as_path())?))
168 });
169 return match result {
170 None => None,
171 Some(Err(e)) => Some(Err(e)),
172 Some(Ok(None)) => continue,
173 Some(Ok(Some(v))) => Some(Ok(v)),
174 };
175 }
176 }
177}