aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
blob: e6b1b78c5d1ea63ed184b0366bc1e0ba8e8d1424 (plain) (blame)
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>, _>>()
}