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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
mod encoded_string;
mod error;
mod main_result;
use std::{
collections::HashSet,
env,
hash::Hash,
io::{self, BufRead, BufReader, BufWriter, Write, Seek, SeekFrom},
process::Command
};
use encoded_string::*;
use error::*;
use main_result::*;
use {
itertools::Itertools,
tempfile::NamedTempFile
};
fn main() -> MainResult {
work().into()
}
fn work() -> Result<(), Error> {
let files = env::args().skip(1).collect::<Vec<String>>();
let dups = duplicate_elements(files.clone());
if !dups.is_empty() {
return Err(Error::DuplicateElems(dups));
}
let mut tmpfile = NamedTempFile::new()?;
let mut writer = BufWriter::new(&tmpfile);
files.iter().try_for_each(|f| encode_to_file(&mut writer, f))?;
writer.flush()?;
drop(writer);
let editor = env::var("EDITOR")
.ok()
.filter(|e| !e.is_empty())
.ok_or(Error::NoEditor)?;
Command::new(&editor)
.arg(tmpfile.path().as_os_str())
.spawn()
.map_err(|err| Error::SpawnFailed(editor, err))?
.wait()?;
tmpfile.seek(SeekFrom::Start(0))?;
let new_files = decode_from_file(&tmpfile)?;
assert_changes(
files.iter().cloned().collect(),
new_files.iter().cloned().collect()
)?;
new_files.iter().for_each(|f| println!("{}", f));
Ok(())
}
fn assert_changes(old: Vec<String>, new: Vec<String>) -> Result<(), Error> {
if old.len() != new.len() {
return Err(Error::BadLengths);
}
let dups = duplicate_elements(new);
if !dups.is_empty() {
return Err(Error::DuplicateElems(dups));
}
Ok(())
}
fn duplicate_elements<T>(iter: T) -> Vec<T::Item>
where
T: IntoIterator,
T::Item: Clone + Eq + Hash
{
let mut uniq = HashSet::new();
iter
.into_iter()
.filter(|x| !uniq.insert(x.clone()))
.unique()
.collect::<Vec<_>>()
}
fn encode_to_file<W: Write>(f: &mut W, s: &str) -> io::Result<()> {
s.chars().try_for_each(|c| {
write!(f, "{}", match c {
'\\' => "\\\\",
'\n' => "\\n",
_ => return write!(f, "{}", c),
}
)
})?;
write!(f, "{}", '\n')
}
fn decode_from_file(tmpfile: &NamedTempFile) -> Result<Vec<String>, io::Error> {
BufReader::new(tmpfile)
.lines()
.map(|r| match r {
Ok(s) => {
let es = EncodedString { s: s.bytes() };
Ok(es.decode())
},
Err(_) => r
})
.collect::<Result<Vec<String>, _>>()
}
|