pub trait Query: Command {
type Response;
// Required methods
fn control(&self) -> Control;
fn parse(&self, payload: &[u8]) -> Result<Self::Response>;
// Provided method
fn run(
&self,
input: &mut Input<'_>,
output: &mut Output<'_>,
) -> Result<Self::Response> { ... }
}
Expand description
A command that receives a response.
Queries are request/response interactions with the terminal. For purposes of
this trait, the response consists of a control followed by the payload
optionally followed by another control (usually BEL
or ST
). The trait
uses a method, and not a constant, for the control, so as to remain
object-safe.
§Example
§The Elaborate Version
To process a query’s response, Scan::read_token()
and ensure that the
Token
is a sequence with a Query::control()
. Then Query::parse
the payload.
// Write the command
output.exec(RequestCursorPosition)?;
// Read the token
let token = input.read_token()?;
// Extract and parse payload
let pos;
if let Token::Sequence(control, payload) = token {
if control == RequestCursorPosition.control() {
pos = RequestCursorPosition.parse(payload)?
} else {
return Err(ErrorKind::BadControl.into());
}
} else {
return Err(ErrorKind::NotASequence.into());
}
§Using Scan::read_sequence
In the above example, generating precise errors requires about as much code
as extracting and parsing the payload. The Scan::read_sequence
method
abstracts over this boilerplate. It certainly helps:
// Write the command
output.exec(RequestCursorPosition)?;
// Read the ANSI escape sequence and extract the payload
let payload = input.read_sequence(RequestCursorPosition.control())?;
// Parse the payload
let pos = RequestCursorPosition.parse(payload)?;
§Using Query::run
While much cleaner, the previous example still is boilerplate. After all,
every query needs to write the request, scan the response for the payload,
and parse the payload. Query::run
abstracts over that:
let pos = RequestCursorPosition.run(&mut input, &mut output)?;
Nice, right? Alas, Query::run
may be slower than needs be when
processing a batch of queries. The method’s documentation addresses this and
other performance considerations.
Required Associated Types§
Required Methods§
Provided Methods§
Sourcefn run(
&self,
input: &mut Input<'_>,
output: &mut Output<'_>,
) -> Result<Self::Response>
fn run( &self, input: &mut Input<'_>, output: &mut Output<'_>, ) -> Result<Self::Response>
Run this query.
This method writes the request to the given output, reads the response from the given input, parses the response payload, returning the result.
§Performance Considerations
Since accessing a connection’s input and output entails acquiring a
mutex each, this method takes the input and output objects as arguments.
That way, the caller controls when to acquire the two objects and incur
the corresponding overhead. As a result, the caller also incurs the
notational overhead of passing two arguments prefixed with &mut
instead of passing one argument prefixed with &
(as the connection
object uses interior mutability). While not ideal, favoring flexibility
and performance over concision seems the right trade-off.
This method is well-suited to running the occasional query. However,
when executing several queries in a row, e.g., when querying a terminal
for its color theme, this method may not be performant, especially when
running in a remote shell. Instead, an application should write all
requests to output before flushing (once) and then process all
responses. Prettypretty’s
Theme::query
does just that. If you check the
source,
it actually implements versions with one, two, and three processing
loops; the query
benchmark
compares their performance.