Skip to content

Commit

Permalink
Merge pull request #30 from matagus/taking-rust-knowledge-to-the-next…
Browse files Browse the repository at this point in the history
…-level-pt2

 Added structs to hold groups and rows, rustfmt config, and modules
  • Loading branch information
matagus authored Oct 26, 2024
2 parents 5e9467e + 64aba71 commit c22ba47
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 128 deletions.
64 changes: 32 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,20 @@ shelve -c 2 example.csv

Done:

2, Fix bug B, Done, Jane Doe, Low
6, Write missing documentation for feature A, Done, Peter Foo, Medium
2, Fix bug B, Jane Doe, Low
6, Write missing documentation for feature A, Peter Foo, Medium

In Progress:

1, Implement feature A, In Progress, John Doe, High
3, Write tests for feature A, In Progress, John Doe, Medium
8, Write tests for feature A, In Progress, John Doe, Low
1, Implement feature A, John Doe, High
3, Write tests for feature A, John Doe, Medium
8, Write tests for feature A, John Doe, Low

To Do:

4, Refactor code, To Do, Jane Doe, High
5, Deploy to production A and B, To Do, John Doe, Low
7, Fix bug C, To Do, Alice Bar, High
4, Refactor code, Jane Doe, High
5, Deploy to production A and B, John Doe, Low
7, Fix bug C, Alice Bar, High
```

Grouping by the `Priority` column (column number 4):
Expand All @@ -75,20 +75,20 @@ shelve -c 4 example.csv

High:

1, Implement feature A, In Progress, John Doe, High
4, Refactor code, To Do, Jane Doe, High
7, Fix bug C, To Do, Alice Bar, High
1, Implement feature A, In Progress, John Doe
4, Refactor code, To Do, Jane Doe
7, Fix bug C, To Do, Alice Bar

Low:

2, Fix bug B, Done, Jane Doe, Low
5, Deploy to production A and B, To Do, John Doe, Low
8, Write tests for feature A, In Progress, John Doe, Low
2, Fix bug B, Done, Jane Doe
5, Deploy to production A and B, To Do, John Doe
8, Write tests for feature A, In Progress, John Doe

Medium:

3, Write tests for feature A, In Progress, John Doe, Medium
6, Write missing documentation for feature A, Done, Peter Foo, Medium
3, Write tests for feature A, In Progress, John Doe
6, Write missing documentation for feature A, Done, Peter Foo
```

Grouping by the `Assignee` column (column number 3):
Expand All @@ -98,23 +98,23 @@ shelve -c 3 example.csv

Alice Bar:

7, Fix bug C, To Do, Alice Bar, High
7, Fix bug C, To Do, High

Jane Doe:

2, Fix bug B, Done, Jane Doe, Low
4, Refactor code, To Do, Jane Doe, High
2, Fix bug B, Done, Low
4, Refactor code, To Do, High

John Doe:

1, Implement feature A, In Progress, John Doe, High
3, Write tests for feature A, In Progress, John Doe, Medium
5, Deploy to production A and B, To Do, John Doe, Low
8, Write tests for feature A, In Progress, John Doe, Low
1, Implement feature A, In Progress, High
3, Write tests for feature A, In Progress, Medium
5, Deploy to production A and B, To Do, Low
8, Write tests for feature A, In Progress, Low

Peter Foo:

6, Write missing documentation for feature A, Done, Peter Foo, Medium
6, Write missing documentation for feature A, Done, Medium
```

The command can also read input from `stdin`:
Expand All @@ -124,20 +124,20 @@ The command can also read input from `stdin`:

High:

1, Implement feature A, In Progress, John Doe, High
4, Refactor code, To Do, Jane Doe, High
7, Fix bug C, To Do, Alice Bar, High
1, Implement feature A, In Progress, John Doe
4, Refactor code, To Do, Jane Doe
7, Fix bug C, To Do, Alice Bar

Low:

2, Fix bug B, Done, Jane Doe, Low
5, Deploy to production A and B, To Do, John Doe, Low
8, Write tests for feature A, In Progress, John Doe, Low
2, Fix bug B, Done, Jane Doe
5, Deploy to production A and B, To Do, John Doe
8, Write tests for feature A, In Progress, John Doe

Medium:

3, Write tests for feature A, In Progress, John Doe, Medium
6, Write missing documentation for feature A, Done, Peter Foo, Medium
3, Write tests for feature A, In Progress, John Doe
6, Write missing documentation for feature A, Done, Peter Foo
```

Or reading multiple files at once:
Expand Down
7 changes: 7 additions & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
indent_style = "Block"
reorder_imports = true
reorder_modules = true
reorder_impl_items = true
max_width = 120
chain_width = 120
attr_fn_like_width = 100
11 changes: 11 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use clap::Parser;

#[derive(Parser)]
#[command(version, about, long_about = None)]
pub struct Cli {
pub filenames: Vec<String>,

/// Column number to group by
#[arg(short, long, default_value = "0")]
pub column_number: Option<u8>,
}
86 changes: 86 additions & 0 deletions src/groups.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
use std::error::Error;
use std::fs::File;

#[derive(Debug)]
pub struct Row {
data: Vec<String>,
index: usize,
}

impl std::fmt::Display for Row {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut cloned_data = self.data.clone();
cloned_data.remove(self.index);
write!(f, "{}", cloned_data.join(", "))
}
}

#[derive(Debug)]
pub struct GroupedData {
groups: BTreeMap<String, Vec<Row>>,
index: usize,
}

impl Row {
fn new(data: Vec<String>, index: usize) -> Self {
Row { data, index }
}
}

impl GroupedData {
fn new(index: usize) -> Self {
GroupedData {
groups: BTreeMap::new(),
index,
}
}

fn process<R: std::io::Read>(&mut self, rdr: &mut csv::Reader<R>) -> Result<(), Box<dyn Error>> {
for result in rdr.records() {
let record = result?;
let key = record[self.index].to_string();
let row = Row::new(record.iter().map(|s| s.to_string()).collect(), self.index);
self.add(&key, row);
}
Ok(())
}

pub fn from_files(filename_vec: &[String], index: usize) -> Result<Self, Box<dyn Error>> {
let mut groups = GroupedData::new(index);

if filename_vec.is_empty() {
let stdin = std::io::stdin().lock();
let mut rdr = csv::Reader::from_reader(stdin);
groups.process(&mut rdr)?;
} else {
for filename in filename_vec {
// Create a CSV reader
let mut rdr = csv::Reader::from_reader(File::open(filename)?);
groups.process(&mut rdr)?;
}
}

Ok(groups)
}

pub fn add(&mut self, group_name: &str, row: Row) {
match self.groups.entry(group_name.to_string()) {
Entry::Occupied(mut entry) => {
entry.get_mut().push(row);
}
Entry::Vacant(entry) => {
entry.insert(vec![row]);
}
}
}

pub fn get_groups(&self) -> Vec<&String> {
self.groups.keys().collect()
}

pub fn get_rows(&self, group_name: &str) -> Option<&Vec<Row>> {
self.groups.get(group_name)
}
}
102 changes: 6 additions & 96 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
use clap::Parser;
use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
use std::error::Error;
use std::fs::File;
use std::io::{self, Write};
use std::process;

#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Cli {
filenames: Vec<String>,

/// Column number to group by
#[arg(short, long, default_value = "0")]
column_number: Option<u8>,
}
mod groups;
use groups::GroupedData;
mod cli;
use cli::Cli;

fn main() {
let cli = Cli::parse();
Expand All @@ -27,69 +19,8 @@ fn main() {
}
}

#[derive(Debug)]
struct Row {
data: Vec<String>,
}

#[derive(Debug)]
struct GroupedData {
groups: BTreeMap<String, Vec<Row>>,
}

impl Row {
fn new(data: Vec<String>) -> Self {
Row { data }
}

fn join_without_col(&self, separator: &str, index: usize) -> String {
let mut data = self.data.clone();
data.remove(index);
data.join(separator)
}
}

impl GroupedData {
fn new() -> Self {
GroupedData {
groups: BTreeMap::new(),
}
}

fn add(&mut self, group_name: &str, row: Row) {
match self.groups.entry(group_name.to_string()) {
Entry::Occupied(mut entry) => {
entry.get_mut().push(row);
}
Entry::Vacant(entry) => {
entry.insert(vec![row]);
}
}
}

fn get_groups(&self) -> Vec<&String> {
self.groups.keys().collect()
}

fn get_rows(&self, group_name: &str) -> Option<&Vec<Row>> {
self.groups.get(group_name)
}
}

fn run(filename_vec: &[String], index: usize) -> Result<(), Box<dyn Error>> {
let mut groups: GroupedData = GroupedData::new();

if filename_vec.is_empty() {
let stdin = std::io::stdin().lock();
let mut rdr = csv::Reader::from_reader(stdin);
process_reader(&mut rdr, &mut groups, index)?;
} else {
for filename in filename_vec {
// Create a CSV reader
let mut rdr = csv::Reader::from_reader(File::open(filename)?);
process_reader(&mut rdr, &mut groups, index)?;
}
}
let groups: GroupedData = GroupedData::from_files(filename_vec, index)?;

// Use a BufWriter to improve performance by reducing the number of write calls
let stdout = io::stdout();
Expand All @@ -100,32 +31,11 @@ fn run(filename_vec: &[String], index: usize) -> Result<(), Box<dyn Error>> {

if let Some(rows) = groups.get_rows(group) {
for row in rows {
let display_row = row.join_without_col(", ", index);
writeln!(stream, "{}", display_row)?;
writeln!(stream, "{}", row)?;
}
}
writeln!(stream)?;
}

Ok(())
}

fn process_reader<R: std::io::Read>(
rdr: &mut csv::Reader<R>,
groups: &mut GroupedData,
index: usize,
) -> Result<(), Box<dyn Error>> {
for result in rdr.records() {
let record = result?;

// Check if the column index is within bounds
if index >= record.len() {
return Err(format!("Column index {} is out of bounds", index).into());
}

let key = record[index].to_string();
let row = Row::new(record.iter().map(|s| s.to_string()).collect());
groups.add(&key, row);
}
Ok(())
}

0 comments on commit c22ba47

Please sign in to comment.