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
-
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>andOption<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,timeReplace
src/main.rswith 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 --quietExpected output:
query: prod-api
Members1#
- filter_fields method Return typed handles for this candidate schema.
Guides#
Pages that discuss Filterable.