1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 03:27:44 +00:00

Merge pull request #2798 from jfinkels/fmt-uresult

fmt: return UResult from uumain() function
This commit is contained in:
Sylvestre Ledru 2021-12-29 21:59:50 +01:00 committed by GitHub
commit a8457bfad6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 81 additions and 59 deletions

View file

@ -16,19 +16,11 @@ use std::fs::File;
use std::io::{stdin, stdout, Write}; use std::io::{stdin, stdout, Write};
use std::io::{BufReader, BufWriter, Read}; use std::io::{BufReader, BufWriter, Read};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError};
use self::linebreak::break_lines; use self::linebreak::break_lines;
use self::parasplit::ParagraphStream; use self::parasplit::ParagraphStream;
macro_rules! silent_unwrap(
($exp:expr) => (
match $exp {
Ok(_) => (),
Err(_) => ::std::process::exit(1),
}
)
);
mod linebreak; mod linebreak;
mod parasplit; mod parasplit;
@ -74,8 +66,9 @@ pub struct FmtOptions {
tabwidth: usize, tabwidth: usize,
} }
#[uucore_procs::gen_uumain]
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
pub fn uumain(args: impl uucore::Args) -> i32 { pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage(); let usage = usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args); let matches = uu_app().usage(&usage[..]).get_matches_from(args);
@ -133,15 +126,20 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
fmt_opts.width = match s.parse::<usize>() { fmt_opts.width = match s.parse::<usize>() {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => {
crash!(1, "Invalid WIDTH specification: {}: {}", s.quote(), e); return Err(USimpleError::new(
1,
format!("Invalid WIDTH specification: {}: {}", s.quote(), e),
));
} }
}; };
if fmt_opts.width > MAX_WIDTH { if fmt_opts.width > MAX_WIDTH {
crash!( return Err(USimpleError::new(
1, 1,
"invalid width: '{}': Numerical result out of range", format!(
fmt_opts.width "invalid width: '{}': Numerical result out of range",
); fmt_opts.width,
),
));
} }
fmt_opts.goal = cmp::min(fmt_opts.width * 94 / 100, fmt_opts.width - 3); fmt_opts.goal = cmp::min(fmt_opts.width * 94 / 100, fmt_opts.width - 3);
}; };
@ -150,13 +148,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
fmt_opts.goal = match s.parse::<usize>() { fmt_opts.goal = match s.parse::<usize>() {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => {
crash!(1, "Invalid GOAL specification: {}: {}", s.quote(), e); return Err(USimpleError::new(
1,
format!("Invalid GOAL specification: {}: {}", s.quote(), e),
));
} }
}; };
if !matches.is_present(OPT_WIDTH) { if !matches.is_present(OPT_WIDTH) {
fmt_opts.width = cmp::max(fmt_opts.goal * 100 / 94, fmt_opts.goal + 3); fmt_opts.width = cmp::max(fmt_opts.goal * 100 / 94, fmt_opts.goal + 3);
} else if fmt_opts.goal > fmt_opts.width { } else if fmt_opts.goal > fmt_opts.width {
crash!(1, "GOAL cannot be greater than WIDTH."); return Err(USimpleError::new(1, "GOAL cannot be greater than WIDTH."));
} }
}; };
@ -164,7 +165,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
fmt_opts.tabwidth = match s.parse::<usize>() { fmt_opts.tabwidth = match s.parse::<usize>() {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => {
crash!(1, "Invalid TABWIDTH specification: {}: {}", s.quote(), e); return Err(USimpleError::new(
1,
format!("Invalid TABWIDTH specification: {}: {}", s.quote(), e),
));
} }
}; };
}; };
@ -197,18 +201,25 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
for para_result in p_stream { for para_result in p_stream {
match para_result { match para_result {
Err(s) => { Err(s) => {
silent_unwrap!(ostream.write_all(s.as_bytes())); ostream
silent_unwrap!(ostream.write_all(b"\n")); .write_all(s.as_bytes())
.map_err_context(|| "failed to write output".to_string())?;
ostream
.write_all(b"\n")
.map_err_context(|| "failed to write output".to_string())?;
} }
Ok(para) => break_lines(&para, &fmt_opts, &mut ostream), Ok(para) => break_lines(&para, &fmt_opts, &mut ostream)
.map_err_context(|| "failed to write output".to_string())?,
} }
} }
// flush the output after each file // flush the output after each file
silent_unwrap!(ostream.flush()); ostream
.flush()
.map_err_context(|| "failed to write output".to_string())?;
} }
0 Ok(())
} }
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {

View file

@ -40,7 +40,11 @@ impl<'a> BreakArgs<'a> {
} }
} }
pub fn break_lines(para: &Paragraph, opts: &FmtOptions, ostream: &mut BufWriter<Stdout>) { pub fn break_lines(
para: &Paragraph,
opts: &FmtOptions,
ostream: &mut BufWriter<Stdout>,
) -> std::io::Result<()> {
// indent // indent
let p_indent = &para.indent_str[..]; let p_indent = &para.indent_str[..];
let p_indent_len = para.indent_len; let p_indent_len = para.indent_len;
@ -54,26 +58,25 @@ pub fn break_lines(para: &Paragraph, opts: &FmtOptions, ostream: &mut BufWriter<
let (w, w_len) = match p_words_words.next() { let (w, w_len) = match p_words_words.next() {
Some(winfo) => (winfo.word, winfo.word_nchars), Some(winfo) => (winfo.word, winfo.word_nchars),
None => { None => {
silent_unwrap!(ostream.write_all(b"\n")); return ostream.write_all(b"\n");
return;
} }
}; };
// print the init, if it exists, and get its length // print the init, if it exists, and get its length
let p_init_len = w_len let p_init_len = w_len
+ if opts.crown || opts.tagged { + if opts.crown || opts.tagged {
// handle "init" portion // handle "init" portion
silent_unwrap!(ostream.write_all(para.init_str.as_bytes())); ostream.write_all(para.init_str.as_bytes())?;
para.init_len para.init_len
} else if !para.mail_header { } else if !para.mail_header {
// for non-(crown, tagged) that's the same as a normal indent // for non-(crown, tagged) that's the same as a normal indent
silent_unwrap!(ostream.write_all(p_indent.as_bytes())); ostream.write_all(p_indent.as_bytes())?;
p_indent_len p_indent_len
} else { } else {
// except that mail headers get no indent at all // except that mail headers get no indent at all
0 0
}; };
// write first word after writing init // write first word after writing init
silent_unwrap!(ostream.write_all(w.as_bytes())); ostream.write_all(w.as_bytes())?;
// does this paragraph require uniform spacing? // does this paragraph require uniform spacing?
let uniform = para.mail_header || opts.uniform; let uniform = para.mail_header || opts.uniform;
@ -88,26 +91,29 @@ pub fn break_lines(para: &Paragraph, opts: &FmtOptions, ostream: &mut BufWriter<
}; };
if opts.quick || para.mail_header { if opts.quick || para.mail_header {
break_simple(p_words_words, &mut break_args); break_simple(p_words_words, &mut break_args)
} else { } else {
break_knuth_plass(p_words_words, &mut break_args); break_knuth_plass(p_words_words, &mut break_args)
} }
} }
// break_simple implements a "greedy" breaking algorithm: print words until // break_simple implements a "greedy" breaking algorithm: print words until
// maxlength would be exceeded, then print a linebreak and indent and continue. // maxlength would be exceeded, then print a linebreak and indent and continue.
fn break_simple<'a, T: Iterator<Item = &'a WordInfo<'a>>>(iter: T, args: &mut BreakArgs<'a>) { fn break_simple<'a, T: Iterator<Item = &'a WordInfo<'a>>>(
iter.fold((args.init_len, false), |l, winfo| { mut iter: T,
args: &mut BreakArgs<'a>,
) -> std::io::Result<()> {
iter.try_fold((args.init_len, false), |l, winfo| {
accum_words_simple(args, l, winfo) accum_words_simple(args, l, winfo)
}); })?;
silent_unwrap!(args.ostream.write_all(b"\n")); args.ostream.write_all(b"\n")
} }
fn accum_words_simple<'a>( fn accum_words_simple<'a>(
args: &mut BreakArgs<'a>, args: &mut BreakArgs<'a>,
(l, prev_punct): (usize, bool), (l, prev_punct): (usize, bool),
winfo: &'a WordInfo<'a>, winfo: &'a WordInfo<'a>,
) -> (usize, bool) { ) -> std::io::Result<(usize, bool)> {
// compute the length of this word, considering how tabs will expand at this position on the line // compute the length of this word, considering how tabs will expand at this position on the line
let wlen = winfo.word_nchars + args.compute_width(winfo, l, false); let wlen = winfo.word_nchars + args.compute_width(winfo, l, false);
@ -119,12 +125,12 @@ fn accum_words_simple<'a>(
); );
if l + wlen + slen > args.opts.width { if l + wlen + slen > args.opts.width {
write_newline(args.indent_str, args.ostream); write_newline(args.indent_str, args.ostream)?;
write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream); write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream)?;
(args.indent_len + winfo.word_nchars, winfo.ends_punct) Ok((args.indent_len + winfo.word_nchars, winfo.ends_punct))
} else { } else {
write_with_spaces(winfo.word, slen, args.ostream); write_with_spaces(winfo.word, slen, args.ostream)?;
(l + wlen + slen, winfo.ends_punct) Ok((l + wlen + slen, winfo.ends_punct))
} }
} }
@ -135,16 +141,16 @@ fn accum_words_simple<'a>(
fn break_knuth_plass<'a, T: Clone + Iterator<Item = &'a WordInfo<'a>>>( fn break_knuth_plass<'a, T: Clone + Iterator<Item = &'a WordInfo<'a>>>(
mut iter: T, mut iter: T,
args: &mut BreakArgs<'a>, args: &mut BreakArgs<'a>,
) { ) -> std::io::Result<()> {
// run the algorithm to get the breakpoints // run the algorithm to get the breakpoints
let breakpoints = find_kp_breakpoints(iter.clone(), args); let breakpoints = find_kp_breakpoints(iter.clone(), args);
// iterate through the breakpoints (note that breakpoints is in reverse break order, so we .rev() it // iterate through the breakpoints (note that breakpoints is in reverse break order, so we .rev() it
let (mut prev_punct, mut fresh) = breakpoints.iter().rev().fold( let result: std::io::Result<(bool, bool)> = breakpoints.iter().rev().try_fold(
(false, false), (false, false),
|(mut prev_punct, mut fresh), &(next_break, break_before)| { |(mut prev_punct, mut fresh), &(next_break, break_before)| {
if fresh { if fresh {
write_newline(args.indent_str, args.ostream); write_newline(args.indent_str, args.ostream)?;
} }
// at each breakpoint, keep emitting words until we find the word matching this breakpoint // at each breakpoint, keep emitting words until we find the word matching this breakpoint
for winfo in &mut iter { for winfo in &mut iter {
@ -167,26 +173,27 @@ fn break_knuth_plass<'a, T: Clone + Iterator<Item = &'a WordInfo<'a>>>(
if winfo_ptr == next_break_ptr { if winfo_ptr == next_break_ptr {
// OK, we found the matching word // OK, we found the matching word
if break_before { if break_before {
write_newline(args.indent_str, args.ostream); write_newline(args.indent_str, args.ostream)?;
write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream); write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream)?;
} else { } else {
// breaking after this word, so that means "fresh" is true for the next iteration // breaking after this word, so that means "fresh" is true for the next iteration
write_with_spaces(word, slen, args.ostream); write_with_spaces(word, slen, args.ostream)?;
fresh = true; fresh = true;
} }
break; break;
} else { } else {
write_with_spaces(word, slen, args.ostream); write_with_spaces(word, slen, args.ostream)?;
} }
} }
(prev_punct, fresh) Ok((prev_punct, fresh))
}, },
); );
let (mut prev_punct, mut fresh) = result?;
// after the last linebreak, write out the rest of the final line. // after the last linebreak, write out the rest of the final line.
for winfo in iter { for winfo in iter {
if fresh { if fresh {
write_newline(args.indent_str, args.ostream); write_newline(args.indent_str, args.ostream)?;
} }
let (slen, word) = slice_if_fresh( let (slen, word) = slice_if_fresh(
fresh, fresh,
@ -199,9 +206,9 @@ fn break_knuth_plass<'a, T: Clone + Iterator<Item = &'a WordInfo<'a>>>(
); );
prev_punct = winfo.ends_punct; prev_punct = winfo.ends_punct;
fresh = false; fresh = false;
write_with_spaces(word, slen, args.ostream); write_with_spaces(word, slen, args.ostream)?;
} }
silent_unwrap!(args.ostream.write_all(b"\n")); args.ostream.write_all(b"\n")
} }
struct LineBreak<'a> { struct LineBreak<'a> {
@ -494,17 +501,21 @@ fn slice_if_fresh(
} }
// Write a newline and add the indent. // Write a newline and add the indent.
fn write_newline(indent: &str, ostream: &mut BufWriter<Stdout>) { fn write_newline(indent: &str, ostream: &mut BufWriter<Stdout>) -> std::io::Result<()> {
silent_unwrap!(ostream.write_all(b"\n")); ostream.write_all(b"\n")?;
silent_unwrap!(ostream.write_all(indent.as_bytes())); ostream.write_all(indent.as_bytes())
} }
// Write the word, along with slen spaces. // Write the word, along with slen spaces.
fn write_with_spaces(word: &str, slen: usize, ostream: &mut BufWriter<Stdout>) { fn write_with_spaces(
word: &str,
slen: usize,
ostream: &mut BufWriter<Stdout>,
) -> std::io::Result<()> {
if slen == 2 { if slen == 2 {
silent_unwrap!(ostream.write_all(b" ")); ostream.write_all(b" ")?;
} else if slen == 1 { } else if slen == 1 {
silent_unwrap!(ostream.write_all(b" ")); ostream.write_all(b" ")?;
} }
silent_unwrap!(ostream.write_all(word.as_bytes())); ostream.write_all(word.as_bytes())
} }