Skip to content

Commit

Permalink
tabled/ Add Locator::by (ByCondition)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhiburt committed Oct 31, 2023
1 parent 9f59d5d commit fbbf779
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 30 deletions.
50 changes: 45 additions & 5 deletions tabled/examples/alphabet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,51 @@
//! * Note how [`Range`] [expression syntax](https://doc.rust-lang.org/reference/expressions/range-expr.html)
//! is used to idiomatically represent the English alphabet.

use std::iter::FromIterator;
fn main() {
use tabled::{
settings::{
location::Locator,
object::{Columns, Object},
Alignment, Modify, Padding,
},
Table, Tabled,
};

use tabled::Table;
#[derive(Tabled)]
struct Reading {
link: &'static str,
comment: &'static str,
}

fn main() {
let table = Table::from_iter(['a'..='z']);
println!("{table}");
let data = [
Reading {
link: "https://www.gnu.org/software/grub/manual/multiboot/multiboot.html",
comment: "todo",
},
Reading {
link: "https://wiki.debian.org/initramfs",
comment: "todo",
},
Reading {
link: "http://jdebp.uk/FGA/efi-boot-process.html",
comment: "todo,2",
},
Reading {
link: "https://wiki.debian.org/UEFI",
comment: "todo,2",
},
];

let mut table = Table::new(data);
table.with(Padding::zero());
table.with(Modify::new(Locator::column("link")).with(Alignment::right()));
table.with(Modify::new(Locator::content("todo")).with("todo,1"));
table.with(
Modify::new(Columns::single(1).intersect(Locator::by(|text| text.contains("todo"))))
.with(Padding::new(4, 0, 0, 0)),
);

let output = table.to_string();

println!("{output}");
}
2 changes: 1 addition & 1 deletion tabled/src/settings/location/by_column_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
/// A name is considered be a value in a first row.
///
/// So even if in reality there's no header, the first row will be considered to be one.
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct ByColumnName<S>(S);

impl<S> ByColumnName<S> {
Expand Down
74 changes: 74 additions & 0 deletions tabled/src/settings/location/by_condition.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use crate::{
grid::config::Entity,
grid::{
config::Position,
records::{ExactRecords, PeekableRecords, Records},
},
settings::location::Location,
settings::object::Object,
};

/// The structure is an implementation of [`Locator`] to search for cells with a specified condition.
#[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct ByCondition<F>(F);

impl<F> ByCondition<F> {
/// Constructs a new object of the structure.
pub fn new(search: F) -> Self
where
F: Fn(&str) -> bool,
{
Self(search)
}
}

impl<F, R> Location<R> for ByCondition<F>
where
F: Fn(&str) -> bool,
R: Records + ExactRecords + PeekableRecords,
{
type Coordinate = Position;
type IntoIter = Vec<Position>;

fn locate(&mut self, records: &R) -> Self::IntoIter {
// todo: can be optimized by creating Iterator
let cond = &self.0;

let mut out = vec![];
for row in 0..records.count_rows() {
for col in 0..records.count_columns() {
let text = records.get_text((row, col));
if cond(text) {
out.push((row, col));
}
}
}

out
}
}

impl<F, R> Object<R> for ByCondition<F>
where
F: Fn(&str) -> bool,
R: Records + ExactRecords + PeekableRecords,
{
type Iter = std::vec::IntoIter<Entity>;

fn cells(&self, records: &R) -> Self::Iter {
// todo: can be optimized by creating Iterator
let cond = &self.0;

let mut out = vec![];
for row in 0..records.count_rows() {
for col in 0..records.count_columns() {
let text = records.get_text((row, col));
if cond(text) {
out.push(Entity::Cell(row, col));
}
}
}

out.into_iter()
}
}
7 changes: 2 additions & 5 deletions tabled/src/settings/location/by_content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@ use crate::{
settings::object::Object,
};

/// The structure is an implementation of [`Locator`] to search for cells with a specified content by it's name.
/// A name is considered be a value in a first row.
///
/// So even if in reality there's no header, the first row will be considered to be one.
#[derive(Debug, Clone, Copy)]
/// The structure is an implementation of [`Locator`] to search for cells with a given content.
#[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct ByContent<S>(S);

impl<S> ByContent<S> {
Expand Down
13 changes: 12 additions & 1 deletion tabled/src/settings/location/locator.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{ByColumnName, ByContent};
use super::{ByColumnName, ByCondition, ByContent};

/// An abstract factory for locations, to be used to find different things on the table.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
Expand All @@ -20,4 +20,15 @@ impl Locator {
{
ByColumnName::new(text)
}

/// Constructs a new location searcher with a specified condition closure.
///
/// Return `true` if it shall be included in output.
/// Otherwise return `false`.
pub fn by<F>(condition: F) -> ByCondition<F>
where
F: Fn(&str) -> bool,
{
ByCondition::new(condition)
}
}
113 changes: 95 additions & 18 deletions tabled/src/settings/location/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
//! # Example
//!
//! ```
//! use tabled::{Table, Tabled};
//! use tabled::settings::{
//! Modify, object::{Object, Rows},
//! location::Locator, Padding, Alignment,
//! use tabled::{
//! settings::{
//! location::Locator,
//! object::{Columns, Object},
//! Alignment, Modify, Padding,
//! },
//! Table, Tabled,
//! };
//!
//! #[derive(Tabled)]
Expand All @@ -24,36 +27,42 @@
//!
//! let mut table = Table::new(data);
//! table.with(Padding::zero());
//! table.with(Modify::new(Locator::content("todo,2").intersect(Rows::last())).with(Alignment::right()));
//! table.with(Modify::new(Locator::content("todo")).with(Alignment::center()));
//! table.with(Modify::new(Locator::column("link")).with(Alignment::right()));
//! table.with(Modify::new(Locator::content("todo")).with("todo,1"));
//! table.with(
//! Modify::new(Columns::single(1).intersect(Locator::by(|text| text.contains("todo"))))
//! .with(Padding::new(4, 0, 0, 0)),
//! );
//!
//! let output = table.to_string();
//!
//! assert_eq!(
//! output,
//! concat!(
//! "+-----------------------------------------------------------------+-------+\n",
//! "|link |comment|\n",
//! "+-----------------------------------------------------------------+-------+\n",
//! "|https://www.gnu.org/software/grub/manual/multiboot/multiboot.html| todo |\n",
//! "+-----------------------------------------------------------------+-------+\n",
//! "|https://wiki.debian.org/initramfs | todo |\n",
//! "+-----------------------------------------------------------------+-------+\n",
//! "|http://jdebp.uk/FGA/efi-boot-process.html |todo,2 |\n",
//! "+-----------------------------------------------------------------+-------+\n",
//! "|https://wiki.debian.org/UEFI | todo,2|\n",
//! "+-----------------------------------------------------------------+-------+",
//! "+-----------------------------------------------------------------+----------+\n",
//! "| link|comment |\n",
//! "+-----------------------------------------------------------------+----------+\n",
//! "|https://www.gnu.org/software/grub/manual/multiboot/multiboot.html| todo,1|\n",
//! "+-----------------------------------------------------------------+----------+\n",
//! "| https://wiki.debian.org/initramfs| todo,1|\n",
//! "+-----------------------------------------------------------------+----------+\n",
//! "| http://jdebp.uk/FGA/efi-boot-process.html| todo,2|\n",
//! "+-----------------------------------------------------------------+----------+\n",
//! "| https://wiki.debian.org/UEFI| todo,2|\n",
//! "+-----------------------------------------------------------------+----------+",
//! ),
//! );
//! ```

// todo: Add .modify method for Table

mod by_column_name;
mod by_condition;
mod by_content;
mod locator;

pub use by_column_name::ByColumnName;
pub use by_condition::ByCondition;
pub use by_content::ByContent;
pub use locator::Locator;

Expand Down Expand Up @@ -211,7 +220,7 @@ mod tests {
grid::config::Entity,
grid::records::vec_records::CellInfo,
grid::records::vec_records::VecRecords,
settings::location::{ByColumnName, ByContent},
settings::location::{ByColumnName, ByCondition, ByContent},
settings::object::Object,
};

Expand Down Expand Up @@ -303,6 +312,70 @@ mod tests {
);
}

#[test]
fn object_by_condition_test() {
let data = [
vec![vec![1, 2, 3], vec![1, 2, 3], vec![1, 2, 3]],
vec![vec![1, 2, 3], vec![1, 1, 3], vec![1, 2, 1]],
vec![vec![1, 1, 3], vec![1, 1, 3], vec![1, 1, 1]],
vec![vec![1, 1, 1], vec![1, 1, 3], vec![1, 1, 1]],
vec![vec![0, 1, 1], vec![1, 1, 3], vec![1, 1, 1]],
vec![vec![0, 0, 0], vec![1, 1, 3], vec![1, 1, 1]],
];

assert_eq!(cells(by_cond("1"), &[]), []);
assert_eq!(cells(by_cond("1"), &[vec![], vec![], vec![]]), []);
assert_eq!(
cells(by_cond("1"), &data[0]),
[Cell(0, 0), Cell(1, 0), Cell(2, 0)]
);
assert_eq!(
cells(by_cond("1"), &data[1]),
[Cell(0, 0), Cell(1, 0), Cell(1, 1), Cell(2, 0), Cell(2, 2)]
);
assert_eq!(
cells(by_cond("1"), &data[2]),
[
Cell(0, 0),
Cell(0, 1),
Cell(1, 0),
Cell(1, 1),
Cell(2, 0),
Cell(2, 1),
Cell(2, 2)
]
);
assert_eq!(
cells(by_cond("1"), &data[3]),
[
Cell(0, 0),
Cell(0, 1),
Cell(0, 2),
Cell(1, 0),
Cell(1, 1),
Cell(2, 0),
Cell(2, 1),
Cell(2, 2)
]
);
assert_eq!(
cells(by_cond("1"), &data[4]),
[
Cell(0, 1),
Cell(0, 2),
Cell(1, 0),
Cell(1, 1),
Cell(2, 0),
Cell(2, 1),
Cell(2, 2)
]
);
assert_eq!(
cells(by_cond("1"), &data[5]),
[Cell(1, 0), Cell(1, 1), Cell(2, 0), Cell(2, 1), Cell(2, 2)]
);
}

fn by_colname(text: &str) -> ByColumnName<&str> {
ByColumnName::new(text)
}
Expand All @@ -311,6 +384,10 @@ mod tests {
ByContent::new(text)
}

fn by_cond(text: &'static str) -> ByCondition<impl Fn(&str) -> bool> {
ByCondition::new(move |content| content == text)
}

fn cells<O>(o: O, data: &[Vec<usize>]) -> Vec<Entity>
where
O: Object<VecRecords<CellInfo<String>>>,
Expand Down

0 comments on commit fbbf779

Please sign in to comment.