Core Library0.xAPI Reference

Choose documentation 3

latest

Current version

latest
English

Early release libtmux is released, and its APIs may still change between releases. This site's structure and URLs may change too.

trait libtmux-rs latest

Filterable

A type with a stable schema that can evaluate portable filter predicates.

Source and package
Qualified name
libtmux::query::Filterable
Module
query
Package
libtmux
Source
crates/libtmux/src/query.rs
trait query.Filterable

Description#

This trait is designed for generated implementations; the example spells out that ABI by hand. Methods prefixed with __filter_ are not ordinary authoring hooks.

Examples#

use std::error::Error as _;
use libtmux::query::{
BoolField, EnumField, FilterEnum, FilterExpressionError,
FilterExpressionErrorKind, Filterable, IntegerField, TextField,
};
use libtmux::query::__private::{self, IntegerKind, Predicate};
enum State {
Ready,
}
impl FilterEnum for State {
const FILTER_VARIANTS: &'static [&'static str] = &["ready"];
fn filter_name(&self) -> &'static str {
match self {
Self::Ready => "ready",
}
}
}
struct Task {
name: &'static [u8],
done: bool,
priority: i8,
retries: u8,
state: State,
}
struct TaskFields {
name: TextField<Task>,
done: BoolField<Task>,
priority: IntegerField<Task, i8>,
retries: IntegerField<Task, u8>,
state: EnumField<Task, State>,
}
impl Filterable for Task {
type Fields = TaskFields;
const FILTER_TARGET: &'static str = "task";
fn filter_fields() -> Self::Fields {
TaskFields {
name: __private::text_field(Self::FILTER_TARGET, "name"),
done: __private::bool_field(Self::FILTER_TARGET, "done"),
priority: __private::integer_field(Self::FILTER_TARGET, "priority"),
retries: __private::integer_field(Self::FILTER_TARGET, "retries"),
state: __private::enum_field(Self::FILTER_TARGET, "state"),
}
}
fn __filter_matches(&self, predicate: &Predicate) -> bool {
Self::__filter_validate(predicate)
.expect("typed field expressions must validate before matching");
match predicate.field() {
"name" => predicate.matches_text(self.name),
"done" => predicate.matches_bool(self.done),
"priority" => predicate.matches_signed(i128::from(self.priority)),
"retries" => predicate.matches_unsigned(u128::from(self.retries)),
"state" => predicate.matches_enum(self.state.filter_name()),
_ => false,
}
}
fn __filter_validate(predicate: &Predicate) -> Result<(), FilterExpressionError> {
match predicate.field() {
"name" => predicate.validate_text(),
"done" => predicate.validate_bool(),
"priority" => predicate.validate_integer(IntegerKind::I8),
"retries" => predicate.validate_integer(IntegerKind::U8),
"state" => predicate.validate_enum(State::FILTER_VARIANTS),
_ => Err(__private::unknown_field_error()),
}
}
}
let task = Task {
name: b"build",
done: false,
priority: -1,
retries: 2,
state: State::Ready,
};
let fields = Task::filter_fields();
assert!(fields.name.eq("build").matches(&task));
assert!(fields.done.eq(false).matches(&task));
assert!(fields.priority.eq(-1).matches(&task));
assert!(fields.retries.eq(2).matches(&task));
assert!(fields.state.eq(State::Ready).matches(&task));
let error = __private::unknown_field_error();
assert_eq!(error.kind(), FilterExpressionErrorKind::UnknownField);
assert!(error.source().is_none());

Generated relation dispatch uses the same opaque predicate ABI for already-loaded Vec<T> and Option<T> fields:

use libtmux::query::{
BoolField, FilterExpressionError, Filterable, ManyRelation, OneRelation,
};
use libtmux::query::__private::{self, Predicate};
struct Child {
done: bool,
}
struct ChildFields {
done: BoolField<Child>,
}
impl Filterable for Child {
type Fields = ChildFields;
const FILTER_TARGET: &'static str = "child";
fn filter_fields() -> Self::Fields {
ChildFields {
done: __private::bool_field(Self::FILTER_TARGET, "done"),
}
}
fn __filter_matches(&self, predicate: &Predicate) -> bool {
assert!(Self::__filter_validate(predicate).is_ok());
match predicate.field() {
"done" => predicate.matches_bool(self.done),
_ => false,
}
}
fn __filter_validate(predicate: &Predicate) -> Result<(), FilterExpressionError> {
match predicate.field() {
"done" => predicate.validate_bool(),
_ => Err(__private::unknown_field_error()),
}
}
}
struct Parent {
children: Vec<Child>,
favorite: Option<Child>,
}
struct ParentFields {
children: ManyRelation<Parent, Child>,
favorite: OneRelation<Parent, Child>,
}
impl Filterable for Parent {
type Fields = ParentFields;
const FILTER_TARGET: &'static str = "parent";
fn filter_fields() -> Self::Fields {
ParentFields {
children: __private::many_relation(Self::FILTER_TARGET, "children"),
favorite: __private::one_relation(Self::FILTER_TARGET, "favorite"),
}
}
fn __filter_matches(&self, predicate: &Predicate) -> bool {
assert!(Self::__filter_validate(predicate).is_ok());
match predicate.field() {
"children" => predicate.matches_many(&self.children),
"favorite" => predicate.matches_one(self.favorite.as_ref()),
_ => false,
}
}
fn __filter_validate(predicate: &Predicate) -> Result<(), FilterExpressionError> {
match predicate.field() {
"children" => predicate.validate_many::<Child>(),
"favorite" => predicate.validate_one::<Child>(),
_ => Err(__private::unknown_field_error()),
}
}
}
let parent = Parent {
children: vec![Child { done: false }, Child { done: true }],
favorite: Some(Child { done: false }),
};
let parent_fields = Parent::filter_fields();
let child_done = Child::filter_fields().done;
assert!(parent_fields.children.any(child_done.eq(false)).matches(&parent));
assert!(
parent_fields
.children
.all(child_done.is_in([false, true]))
.matches(&parent)
);
assert!(
parent_fields
.children
.none(child_done.not_in([false, true]))
.matches(&parent)
);
assert!(parent_fields.favorite.is(child_done.eq(false)).matches(&parent));

Filter snapshots with typed fields. Match a session name prefix without changing the source snapshot. Use Rust 1.85 or newer with Cargo, and tmux 3.3a or newer, on a Unix system. In an empty directory, create a project and add the published crates:

Terminal window
$ cargo init --name libtmux-api-example &&
cargo add libtmux@0.1.2 tempfile@3.20 &&
cargo add tokio@1.40 --features macros,rt,time

Replace src/main.rs with this complete program. It owns a private tmux socket, applies a ten-second task deadline, and reports both task and cleanup errors. Server::kill() stops its daemon; Server::shutdown() closes its client executor. Successful cleanup removes the temporary directory even when the task fails. View source

//! Filter native snapshots with typed field expressions.
use std::error::Error;
use std::time::Duration;
use libtmux::query::{Filterable as _, QueryIteratorExt as _};
use libtmux::{NewSessionOptions, Server, Session};
type ExampleError = Box<dyn Error>;
fn check(condition: bool, message: &str) -> Result<(), ExampleError> {
if condition {
Ok(())
} else {
Err(message.into())
}
}
async fn demonstrate(server: &Server) -> Result<(), ExampleError> {
server
.new_session(NewSessionOptions::new("prod-api").command("cat"))
.await?;
server
.new_session(NewSessionOptions::new("dev-api").command("cat"))
.await?;
let sessions = server.sessions().await?;
let fields = Session::filter_fields();
let production = fields.session_name.starts_with("prod-");
let selected = sessions.iter().matching(&production).exactly_one()?;
check(
selected.name().as_bytes() == b"prod-api",
"wrong filtered session",
)?;
check(sessions.len() == 2, "filtering changed the source snapshot")?;
println!("query: prod-api");
Ok(())
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), ExampleError> {
let root = std::path::Path::new("/tmp/libtmux-rs-dev");
std::fs::create_dir_all(root)?;
let directory = tempfile::Builder::new()
.prefix("api-query-")
.tempdir_in(root)?;
std::fs::write(
directory.path().join("owner"),
std::process::id().to_string(),
)?;
let server = Server::builder()
.socket_path(directory.path().join("tmux.sock"))
.config_file("/dev/null")
.default_timeout(Duration::from_secs(5))
.build()?;
let outcome = tokio::time::timeout(Duration::from_secs(10), demonstrate(&server)).await;
// Stop the owned daemon before closing the client executor.
let killed = server.kill().await;
let closed = server.shutdown().await;
let cleanup_failed = killed.is_err() || closed.is_err();
let mut failures = Vec::new();
match outcome {
Ok(Ok(())) => {}
Ok(Err(error)) => failures.push(format!("example failed: {error}")),
Err(error) => failures.push(format!("example deadline: {error}")),
}
if let Err(error) = killed {
failures.push(format!("daemon cleanup: {error}"));
}
if let Err(error) = closed {
failures.push(format!("executor cleanup: {error}"));
}
if cleanup_failed {
let retained = directory.keep();
failures.push(format!("inspect retained directory {}", retained.display()));
} else if let Err(error) = directory.close() {
failures.push(format!("directory cleanup: {error}"));
}
if failures.is_empty() {
Ok(())
} else {
Err(failures.join("; ").into())
}
}

Run the consumer. Success exits with status 0; setup, operation, deadline or cleanup failures exit unsuccessfully and print diagnostics to stderr:

Terminal window
$ cargo run --quiet

Expected output:

query: prod-api

Members1#

  • filter_fields method Return typed handles for this candidate schema.

Guides#

Pages that discuss Filterable.

Esc

Type to search.

Reference previewOpen reference