diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/error.rs | 63 | ||||
| -rw-r--r-- | src/main.rs | 114 | 
2 files changed, 66 insertions, 111 deletions
| diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index e592e50..0000000 --- a/src/error.rs +++ /dev/null @@ -1,63 +0,0 @@ -use std::{ -	env, -	ffi::OsString, -	fmt::{self, Display, Formatter}, -	io, -	path::PathBuf, -}; - -pub enum Error { -	BadArgs(Option<lexopt::Error>), -	BadDecoding(String), -	BadLengths, -	DuplicateInput(PathBuf), -	DuplicateOutput(PathBuf), -	IO(io::Error), -	Nop, -	SpawnFailed(OsString, io::Error), -} - -impl Display for Error { -	fn fmt(&self, f: &mut Formatter) -> fmt::Result { -		let p = env::args().next().unwrap(); -		match self { -			Self::BadArgs(o) => { -				if let Some(v) = o { -					writeln!(f, "{p}: {v}")?; -				} -				writeln!(f, "Usage: {p} [-0eiv] command [argument ...]") -			} -			Self::BadDecoding(s) => writeln!(f, "{p}: Decoding the file “{s}” failed!"), -			Self::BadLengths => writeln!(f, "{p}: Files have been added or removed during editing"), -			Self::DuplicateInput(s) => writeln!( -				f, -				"{p}: Input file “{}” specified more than once", -				s.to_string_lossy() -			), -			Self::DuplicateOutput(s) => writeln!( -				f, -				"{p}: Output file “{}” specified more than once", -				s.to_string_lossy() -			), -			Self::IO(e) => writeln!(f, "{p}: {e}"), -			Self::Nop => Ok(()), -			Self::SpawnFailed(ed, e) => writeln!( -				f, -				"{p}: Failed to spawn utility “{}”: {e}", -				ed.to_string_lossy() -			), -		} -	} -} - -impl From<io::Error> for Error { -	fn from(e: io::Error) -> Self { -		Self::IO(e) -	} -} - -impl From<lexopt::Error> for Error { -	fn from(e: lexopt::Error) -> Self { -		Self::BadArgs(Some(e)) -	} -} diff --git a/src/main.rs b/src/main.rs index 383175d..68f8d0a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -mod error; -  use std::{  	cmp::Reverse,  	collections::{hash_map::DefaultHasher, HashSet}, @@ -10,16 +8,14 @@ use std::{  	io::{self, BufRead, BufReader, BufWriter, Write},  	iter,  	path::{Component, Path, PathBuf}, -	process::{Command, Stdio}, +	process::{Command, Stdio, self},  };  use { -	proxit::MainResult, +	cerm::{err, warn},  	tempfile::tempdir,  }; -use error::Error; -  #[derive(Default)]  struct Flags {  	pub dryrun: bool, @@ -29,27 +25,37 @@ struct Flags {  	pub verbose: bool,  } -fn main() -> MainResult<(), Error> { -	work().into() +fn usage(bad_flags: Option<lexopt::Error>) -> ! { +	let p = env::args().next().unwrap(); +	if let Some(e) = bad_flags { +		warn!("{e}"); +	} +	eprintln!("Usage: {p} [-0eiv] command [argument ...]"); +	process::exit(1);  } -fn work() -> Result<(), Error> { -	let (flags, rest) = parse_args()?; -	let (cmd, args) = rest.split_first().ok_or(Error::BadArgs(None))?; +fn main() { +	if let Err(e) = work() { +		err!("{e}"); +	} +} + +fn work() -> Result<(), io::Error> { +	let (flags, rest) = match parse_args() { +		Ok(a) => a, +		Err(e) => usage(Some(e)) +	}; +	let (cmd, args) = rest.split_first().unwrap_or_else(|| usage(None));  	// Collect sources from standard input -	let srcs = io::stdin() +	let srcs: Vec<_> = io::stdin()  		.lines() -		.map(|l| { -			l.map_err(Error::from).and_then(|l| { -				if l.is_empty() { -					Err(Error::BadArgs(None)) -				} else { -					Ok(l) -				} -			}) +		.map(|x| match x { +			Err(e) => { err!("{e}"); }, +			Ok(l) if l.is_empty() => usage(None), +			Ok(l) => l,  		}) -		.collect::<Result<Vec<String>, Error>>()?; +		.collect();  	// Spawn the child process  	let mut child = Command::new(cmd) @@ -57,21 +63,25 @@ fn work() -> Result<(), Error> {  		.stdin(Stdio::piped())  		.stdout(Stdio::piped())  		.spawn() -		.map_err(|e| Error::SpawnFailed(cmd.to_owned(), e))?; +		.unwrap_or_else(|e| { +			err!("Failed to spawn utility “{}”: {e}", cmd.to_str().unwrap()); +		});  	// Pass the source files to the child process. -	// TODO: Don’t use expect; create a custom error  	{  		let ci = child  			.stdin  			.take() -			.expect("Could not open the child process’ stdin"); +			.unwrap_or_else(|| { +				err!("Could not open the child process’ stdin"); +			});  		let mut ci = BufWriter::new(ci);  		if flags.encode {  			srcs.iter()  				.try_for_each(|src| writeln!(ci, "{}", encode_string(src)))?;  		} else { -			srcs.iter().try_for_each(|src| writeln!(ci, "{}", src))?; +			srcs.iter() +				.try_for_each(|src| writeln!(ci, "{}", src))?;  		}  	} @@ -81,13 +91,15 @@ fn work() -> Result<(), Error> {  		let co = child  			.stdout  			.take() -			.expect("Could not open the child process’ stdout."); +			.unwrap_or_else(|| { +				err!("Count not open the child process’ stdout."); +			});  		let co = BufReader::new(co);  		// TODO: Don’t allocate an intermediary String per line, by using the BufReader buffer. -		co.lines().try_for_each(|dst| -> Result<(), Error> { +		co.lines().try_for_each(|dst| -> Result<(), io::Error> {  			if flags.encode { -				dsts.push(decode_string(&dst?)?); +				dsts.push(decode_string(&dst?));  			} else {  				dsts.push(dst?);  			} @@ -95,14 +107,14 @@ fn work() -> Result<(), Error> {  		})?;  		if dsts.len() != srcs.len() { -			return Err(Error::BadLengths); +			err!("Files have been added or removed during editing");  		}  	}  	/* If the process failed, it is expected to print an error message; as such,  	   we exit directly. */  	if !child.wait()?.success() { -		return Err(Error::Nop); +		process::exit(1);  	}  	let mut uniq_srcs: HashSet<PathBuf> = HashSet::with_capacity(srcs.len()); @@ -112,15 +124,15 @@ fn work() -> Result<(), Error> {  	let mut ps = srcs  		.iter()  		.zip(dsts) -		.map(|(s, d)| -> Result<(PathBuf, PathBuf, PathBuf), Error> { +		.map(|(s, d)| -> Result<(PathBuf, PathBuf, PathBuf), io::Error> {  			let s = fs::canonicalize(s)?;  			let d = env::current_dir()?.join(Path::new(&d));  			let d = normalize_path(&d);  			if !uniq_srcs.insert(s.clone()) { -				Err(Error::DuplicateInput(s)) +				err!("Input file “{}” specified more than once", s.to_string_lossy());  			} else if !uniq_dsts.insert(d.clone()) { -				Err(Error::DuplicateOutput(d)) +				err!("Output file “{}” specified more than once", d.to_string_lossy());  			} else {  				let mut hasher = DefaultHasher::new();  				s.hash(&mut hasher); @@ -129,17 +141,17 @@ fn work() -> Result<(), Error> {  				Ok((s, t, d))  			}  		}) -		.collect::<Result<Vec<_>, Error>>()?; +		.collect::<Result<Vec<_>, io::Error>>()?;  	/* Sort the src/dst pairs so that the sources with the longest componenets  	   come first. */  	ps.sort_by_key(|s| Reverse(s.0.components().count()));  	for (s, t, _) in ps.iter() { -		move_path(&flags, &s, &t)?; +		move_path(&flags, &s, &t);  	}  	for (_, t, d) in ps.iter().rev() { -		move_path(&flags, &t, &d)?; +		move_path(&flags, &t, &d);  	}  	Ok(()) @@ -188,7 +200,7 @@ fn encode_string(s: &str) -> String {  		.collect::<String>()  } -fn decode_string(s: &str) -> Result<String, Error> { +fn decode_string(s: &str) -> String {  	let mut pv = false;  	s.chars() @@ -215,7 +227,9 @@ fn decode_string(s: &str) -> Result<String, Error> {  		})  		.filter_map(Result::transpose)  		.collect::<Result<String, ()>>() -		.map_err(|()| Error::BadDecoding(s.to_string())) +		.unwrap_or_else(|_| { +			err!("Decoding the file “{}” failed", s); +		})  }  /* Blatantly stolen from the Cargo source code.  This is MIT licensed. */ @@ -246,25 +260,29 @@ fn normalize_path(path: &Path) -> PathBuf {  	ret  } -fn move_path(flags: &Flags, from: &PathBuf, to: &PathBuf) -> io::Result<()> { +fn move_path(flags: &Flags, from: &PathBuf, to: &PathBuf) {  	if flags.verbose {  		println!("{} -> {}", from.as_path().display(), to.as_path().display());  	}  	if !flags.dryrun { -		copy_and_remove_file_or_dir(&from, &to)?; +		copy_and_remove_file_or_dir(&from, &to).unwrap_or_else(|(f, e)| { +			err!("{}: {e}", f.to_string_lossy()); +		});  	} - -	Ok(())  } -fn copy_and_remove_file_or_dir<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> { -	let data = fs::metadata(&from)?; +fn copy_and_remove_file_or_dir<'a>( +	from: &'a PathBuf, +	to: &'a PathBuf +) -> Result<(), (&'a PathBuf, io::Error)> { +	let data = fs::metadata(&from).map_err(|e| (from, e))?;  	if data.is_dir() { -		fs::create_dir(&to)?; -		fs::remove_dir(&from) +		fs::create_dir(&to).map_err(|e| (to, e))?; +		fs::remove_dir(&from).map_err(|e| (from, e))?  	} else { -		fs::copy(&from, &to)?; -		fs::remove_file(&from) +		fs::copy(&from, &to).map_err(|e| (to, e))?; +		fs::remove_file(&from).map_err(|e| (from, e))?  	} +	Ok(())  } |