From b3a3c72949d1f65209a8b3f26da573e8935630ff Mon Sep 17 00:00:00 2001 From: Braeden Sowinski Date: Thu, 1 Oct 2026 18:12:33 -0700 Subject: [PATCH] patch lab 1 feedback + do lab 02 --- labs/01/sort.rs | 3 +- labs/02/cut.rs | 106 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 labs/02/cut.rs diff --git a/labs/01/sort.rs b/labs/01/sort.rs index 49498fc..bd7c3ba 100644 --- a/labs/01/sort.rs +++ b/labs/01/sort.rs @@ -22,9 +22,8 @@ fn main() { }; let mut records: Vec<(String, String, u8)> = input.lines().filter_map(record).collect(); - records.sort(); + records.sort_by(|a, b| (a.2, &a.1, &a.0).cmp(&(b.2, &b.1, &b.0))); records.dedup(); - records.sort_by_key(|r| r.2); for (first_name, last_name, score) in records { println!("{} {}, {}", score, last_name, first_name); diff --git a/labs/02/cut.rs b/labs/02/cut.rs new file mode 100644 index 0000000..6d82e0f --- /dev/null +++ b/labs/02/cut.rs @@ -0,0 +1,106 @@ +use std::env; +use std::io; +use std::process; + +enum Range { + Nth(usize), + From(usize), + Between(usize, usize), + To(usize), +} + +impl Range { + fn parse(s: &str) -> Option { + match s.split_once('-') { + None => Some(Range::Nth(number(s)?)), + Some(("", m)) => Some(Range::To(number(m)?)), + Some((n, "")) => Some(Range::From(number(n)?)), + Some((n, m)) => { + let (n, m) = (number(n)?, number(m)?); + if n > m { + return None; + } + Some(Range::Between(n, m)) + } + } + } + + fn contains(&self, n: usize) -> bool { + match *self { + Range::Nth(x) => n == x, + Range::From(x) => n >= x, + Range::Between(x, y) => n >= x && n <= y, + Range::To(y) => n <= y, + } + } +} + +// str::parse accepts a leading '+', so check digits ourselves +fn number(s: &str) -> Option { + if s.is_empty() || !s.chars().all(|c| c.is_ascii_digit()) { + return None; + } + + let n: usize = s.parse().ok()?; + if n == 0 { + return None; + } + + Some(n) +} + +fn parse_args(arg: &str) -> Option<(bool, Vec)> { + let fields = match arg.get(..2)? { + "-c" => false, + "-f" => true, + _ => return None, + }; + + let ranges = arg[2..] + .split(',') + .map(Range::parse) + .collect::>>()?; + + Some((fields, ranges)) +} + +fn selected(ranges: &[Range], n: usize) -> bool { + ranges.iter().any(|r| r.contains(n)) +} + +fn cut(line: &str, fields: bool, ranges: &[Range]) -> String { + if fields { + line.split('\t') + .enumerate() + .filter(|(i, _)| selected(ranges, i + 1)) + .map(|(_, f)| f) + .collect::>() + .join("\t") + } else { + line.chars() + .enumerate() + .filter(|(i, _)| selected(ranges, i + 1)) + .map(|(_, c)| c) + .collect() + } +} + +fn main() { + let args: Vec = env::args().skip(1).collect(); + + let (fields, ranges) = match args.as_slice() { + [arg] => parse_args(arg), + _ => None, + } + .unwrap_or_else(|| { + eprintln!("usage: cut -c | -f"); + process::exit(1); + }); + + let input = io::read_to_string(io::stdin()).unwrap_or_default(); + + for line in input.split_inclusive('\n') { + let body = line.trim_end_matches(['\n', '\r']); + print!("{}{}", cut(body, fields, &ranges), &line[body.len()..]); + } +}