Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tabled/ Add Content locator ByContent #367

Merged
merged 13 commits into from
Oct 31, 2023
5 changes: 5 additions & 0 deletions tabled/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,11 @@ name = "chess"
path = "examples/chess.rs"
required-features = ["std"]

[[example]]
name = "target_content"
path = "examples/target_content.rs"
required-features = ["std", "derive"]

[features]
default = ["derive", "macros"]
std = ["papergrid/std"]
Expand Down
2 changes: 1 addition & 1 deletion tabled/examples/disable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use tabled::{
settings::{
locator::ByColumnName,
location::ByColumnName,
style::{Border, Style},
Disable, Modify,
},
Expand Down
61 changes: 61 additions & 0 deletions tabled/examples/target_content.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//! This example demonstrates using the [`Color`] [setting](tabled::settings) to
//! stylize text, backgrounds, and borders.
//!
//! * 🚩 This example requires the `color` feature.
//!
//! * Note how [`Format::content()`] is used to break out [`CellOption`]
//! specifications. This is helpful for organizing extensive [`Table`] configurations.

use tabled::{
settings::{location::ByContent, style::Style, Color, Modify},
Table, Tabled,
};

#[derive(Tabled)]
struct Job {
title: String,
#[tabled(display_with = "JobStatus::as_string")]
status: JobStatus,
}

impl Job {
fn new(title: &str, status: JobStatus) -> Self {
Self {
title: title.to_string(),
status,
}
}
}

enum JobStatus {
Open,
Closed,
}

impl JobStatus {
fn as_string(&self) -> &'static str {
match self {
JobStatus::Open => "open",
JobStatus::Closed => "closed",
}
}
}

fn main() {
let data = vec![
Job::new("C Developer", JobStatus::Open),
Job::new("Rust Developer", JobStatus::Closed),
Job::new("Kernel Developer", JobStatus::Open),
];

let color_open_jobs = Color::BG_WHITE | Color::FG_BLACK;
let color_closed_jobs = Color::BG_GREEN | Color::FG_BLACK;

let mut table = Table::new(data);
table
.with(Style::empty())
.with(Modify::list(ByContent::new("open"), color_open_jobs))
.with(Modify::list(ByContent::new("closed"), color_closed_jobs));

println!("{table}");
}
8 changes: 4 additions & 4 deletions tabled/src/settings/disable/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use std::marker::PhantomData;

use crate::{
grid::records::{ExactRecords, Records, Resizable},
settings::{locator::Locator, TableOption},
settings::{location::Location, TableOption},
};

/// Disable removes particular rows/columns from a [`Table`].
Expand Down Expand Up @@ -66,7 +66,7 @@ impl<L> Disable<L, TargetColumn> {
/// - [`ByColumnName`]
///
/// ```rust
/// use tabled::{builder::Builder, settings::{Disable, locator::ByColumnName, object::Columns}};
/// use tabled::{builder::Builder, settings::{Disable, location::ByColumnName, object::Columns}};
///
/// let mut builder = Builder::default();
///
Expand Down Expand Up @@ -151,7 +151,7 @@ pub struct TargetColumn;

impl<L, R, D, C> TableOption<R, D, C> for Disable<L, TargetColumn>
where
for<'a> L: Locator<&'a R, Coordinate = usize>,
L: Location<R, Coordinate = usize>,
R: Records + Resizable,
{
fn change(mut self, records: &mut R, _: &mut C, _: &mut D) {
Expand All @@ -174,7 +174,7 @@ where

impl<L, R, D, C> TableOption<R, D, C> for Disable<L, TargetRow>
where
for<'a> L: Locator<&'a R, Coordinate = usize>,
L: Location<R, Coordinate = usize>,
R: ExactRecords + Resizable,
{
fn change(mut self, records: &mut R, _: &mut C, _: &mut D) {
Expand Down
56 changes: 56 additions & 0 deletions tabled/src/settings/location/by_column_name.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use crate::{
grid::config::Entity,
grid::records::{ExactRecords, PeekableRecords, Records},
settings::location::Location,
settings::object::Object,
};

/// The structure is an implementation of [`Locator`] to search for a column 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, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct ByColumnName<S>(S);

impl<S> ByColumnName<S> {
/// Constructs a new object of the structure.
pub fn new(text: S) -> Self
where
S: AsRef<str>,
{
Self(text)
}
}

impl<R, S> Location<R> for ByColumnName<S>
where
S: AsRef<str>,
R: Records + ExactRecords + PeekableRecords,
{
type Coordinate = usize;
type IntoIter = Vec<usize>;

fn locate(&mut self, records: &R) -> Self::IntoIter {
// todo: can be optimized by creating Iterator
(0..records.count_columns())
.filter(|col| records.get_text((0, *col)) == self.0.as_ref())
.collect::<Vec<_>>()
}
}

impl<S, R> Object<R> for ByColumnName<S>
where
S: AsRef<str>,
R: Records + PeekableRecords + ExactRecords,
{
type Iter = std::vec::IntoIter<Entity>;

fn cells(&self, records: &R) -> Self::Iter {
// todo: can be optimized by creating Iterator
(0..records.count_columns())
.filter(|col| records.get_text((0, *col)) == self.0.as_ref())
.map(Entity::Column)
.collect::<Vec<_>>()
.into_iter()
}
}
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()
}
}
74 changes: 74 additions & 0 deletions tabled/src/settings/location/by_content.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 given content.
#[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct ByContent<S>(S);

impl<S> ByContent<S> {
/// Constructs a new object of the structure.
pub fn new(text: S) -> Self
where
S: AsRef<str>,
{
Self(text)
}
}

impl<R, S> Location<R> for ByContent<S>
where
S: AsRef<str>,
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 text = self.0.as_ref();

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

out
}
}

impl<S, R> Object<R> for ByContent<S>
where
S: AsRef<str>,
R: Records + PeekableRecords + ExactRecords,
{
type Iter = std::vec::IntoIter<Entity>;

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

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

out.into_iter()
}
}
34 changes: 34 additions & 0 deletions tabled/src/settings/location/locator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
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)]
pub struct Locator;

impl Locator {
/// Constructs a new location searcher for a cells with a given content.
pub fn content<S>(text: S) -> ByContent<S>
where
S: AsRef<str>,
{
ByContent::new(text)
}

/// Constructs a new location searcher for a column by its header.
pub fn column<S>(text: S) -> ByColumnName<S>
where
S: AsRef<str>,
{
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)
}
}
Loading
Loading