prettytty

Trait Query

Source
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§

Source

type Response

The type of the response data.

Required Methods§

Source

fn control(&self) -> Control

Get the response’s control.

Source

fn parse(&self, payload: &[u8]) -> Result<Self::Response>

Parse the payload into a response object.

Provided Methods§

Source

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.

Implementations on Foreign Types§

Source§

impl<Q: Query + ?Sized> Query for &Q

A borrowed query is a query.

Source§

type Response = <Q as Query>::Response

Source§

fn control(&self) -> Control

Source§

fn parse(&self, payload: &[u8]) -> Result<Self::Response>

Source§

impl<Q: Query + ?Sized> Query for Box<Q>

A boxed query is a query.

Source§

type Response = <Q as Query>::Response

Source§

fn control(&self) -> Control

Source§

fn parse(&self, payload: &[u8]) -> Result<Self::Response>

Implementors§