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
|
use {
super::types::Die,
std::{borrow::ToOwned, fs::Metadata, io::ErrorKind, path::Path},
tokio::{
fs::{rename, File, OpenOptions},
io::{AsyncReadExt, AsyncWriteExt, BufWriter},
},
uuid::Uuid,
};
pub struct Slurpee {
pub meta: Metadata,
pub content: String,
}
pub async fn slurp(path: &Path) -> Result<Slurpee, Die> {
let mut fd = File::open(path)
.await
.map_err(|e| Die::IO(path.to_owned(), e.kind()))?;
let meta = fd
.metadata()
.await
.map_err(|e| Die::IO(path.to_owned(), e.kind()))?;
let content = if meta.is_file() {
let mut s = String::default();
match fd.read_to_string(&mut s).await {
Ok(_) => s,
Err(err) if err.kind() == ErrorKind::InvalidData => s,
Err(err) => return Err(Die::IO(path.to_owned(), err.kind())),
}
} else {
String::default()
};
Ok(Slurpee { meta, content })
}
pub async fn spit(canonical: &Path, meta: &Metadata, text: &str) -> Result<(), Die> {
let uuid = Uuid::new_v4().as_simple().to_string();
let mut file_name = canonical
.file_name()
.map(ToOwned::to_owned)
.unwrap_or_default();
file_name.push("___");
file_name.push(uuid);
let tmp = canonical.with_file_name(file_name);
let fd = OpenOptions::new()
.create_new(true)
.write(true)
.open(&tmp)
.await
.map_err(|e| Die::IO(tmp.clone(), e.kind()))?;
fd.set_permissions(meta.permissions())
.await
.map_err(|e| Die::IO(tmp.clone(), e.kind()))?;
let mut writer = BufWriter::new(fd);
writer
.write_all(text.as_bytes())
.await
.map_err(|e| Die::IO(tmp.clone(), e.kind()))?;
writer
.flush()
.await
.map_err(|e| Die::IO(tmp.clone(), e.kind()))?;
rename(&tmp, &canonical)
.await
.map_err(|e| Die::IO(canonical.to_owned(), e.kind()))?;
Ok(())
}
|