Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Filtering

cols includes a built-in expression language for filtering lines. Parse an expression, then evaluate it against each line’s data.

Basic usage

#![allow(unused)]
fn main() {
use cols::Filter;

let filter = Filter::parse(r#"SIZE > 1G && TYPE == "disk""#).unwrap();

let matches = filter.evaluate(|column_name| match column_name {
    "SIZE" => Some("4294967296".into()), // 4G in bytes
    "TYPE" => Some("disk".into()),
    _ => None,
}).unwrap();

assert!(matches);
}

The evaluate closure maps column names to their string values for the current row. Return None for unknown columns (treated as empty string).

Operators

Comparison

OperatorWord formMeaning
==EQEqual
!=NENot equal
<LTLess than
<=LELess or equal
>GTGreater than
>=GEGreater or equal

Regex

Regex operators require the regex feature:

cols = { version = "0.2", features = ["regex"] }
OperatorMeaning
=~Matches regex
!~Does not match regex
Filter::parse(r#"NAME =~ "sd[a-z]""#).unwrap();

Boolean

OperatorWord formMeaning
&&ANDLogical and
||ORLogical or
!NOTLogical not

Parentheses control precedence: (A || B) && C.

Literals

  • Integers: 42, 0
  • Integer suffixes: 1K (1024), 2M, 1G, 4T — optionally with iB suffix (1KiB)
  • Floats: 3.14, 0.5
  • Strings: "hello", 'world'
  • Booleans: true, false

Column references

Bare identifiers are column name references (called “holders”):

SIZE > 1G

Here SIZE is resolved via the closure at evaluation time. A bare holder is truthy if the column has a non-empty value.

Extracting column names

#![allow(unused)]
fn main() {
let filter = Filter::parse(r#"SIZE > 1G && TYPE == "disk""#).unwrap();
let columns = filter.holders(); // ["SIZE", "TYPE"]
}

Useful for knowing which columns a filter depends on.

Filtering at print time

The simplest way to filter is to set a filter expression on the table. Non-matching lines are skipped during printing. Column names in the expression are resolved automatically by matching against column headers.

    let mut t = fruit_table();
    t.filter_set(r#"COLOR == "red""#).unwrap();
FRUIT  PRICE COLOR
apple   1.20 red
cherry  3.00 red

The filter works with all output modes. Here’s the same filter as CSV:

FRUIT,PRICE,COLOR
apple,1.20,red
cherry,3.00,red

Call table.filter_clear() to remove the filter and show all lines again.

Custom resolver

By default, column names in the filter expression are matched against column headers. If you need custom resolution (e.g. computed values or renamed columns), set a resolver:

table.filter_resolver_set(|line, col_name| match col_name {
    "BYTES" => line.data_get(1).map(String::from),
    _ => None,
});

Manual filtering

You can also filter manually by iterating lines and building a new table. This gives full control over the filtering logic.

/// Helper: build a device table for filter demos.
fn device_table() -> Table {
    let mut t = Table::new();
    t.new_column("NAME");
    t.new_column("TYPE");
    t.new_column("SIZE");

    let devices = [
        ("sda", "disk", "500107862016"),     // 500G
        ("sda1", "part", "268435456"),       // 256M
        ("sda2", "part", "499839426560"),    // ~465G
        ("sdb", "disk", "2000398934016"),    // 2T
        ("sdb1", "part", "1073741824"),      // 1G
        ("sdb2", "part", "1999325192192"),   // ~1.8T
        ("nvme0n1", "disk", "512110190592"), // 512G
        ("sr0", "rom", "0"),
        ("loop0", "loop", "109051904"), // 104M
    ];

    for (name, typ, size) in devices {
        let row = t.new_line(None);
        t.line_mut(row)
            .data_set(0, name)
            .data_set(1, typ)
            .data_set(2, size);
    }

    t
}

Filter by type

fn main() {
    let source = device_table();
    let filtered = filter_table(&source, r#"TYPE == "disk""#);

    print_table(&filtered, &mut std::io::stdout().lock()).unwrap();
}
NAME    TYPE SIZE
sda     disk 500107862016
sdb     disk 2000398934016
nvme0n1 disk 512110190592

Filter by size (with integer suffix)

The expression SIZE > 1G uses the suffix G (1024^3). The filter automatically parses the cell’s string value as an integer for comparison.

fn main() {
    let source = device_table();
    let filtered = filter_table(&source, "SIZE > 1G");

    print_table(&filtered, &mut std::io::stdout().lock()).unwrap();
}
NAME    TYPE SIZE
sda     disk 500107862016
sda2    part 499839426560
sdb     disk 2000398934016
sdb2    part 1999325192192
nvme0n1 disk 512110190592

Combined filter

fn main() {
    let source = device_table();
    let filtered = filter_table(&source, r#"TYPE == "disk" && SIZE > 1G"#);

    print_table(&filtered, &mut std::io::stdout().lock()).unwrap();
}
NAME    TYPE SIZE
sda     disk 500107862016
sdb     disk 2000398934016
nvme0n1 disk 512110190592