anes-0.1.6/benches/bench_main.rs010064400007650000024000000001371357023236000147250ustar0000000000000000use criterion::criterion_main; mod benchmarks; criterion_main!(benchmarks::parser::benches); anes-0.1.6/benches/benchmarks/mod.rs010064400007650000024000000000201357023236000155250ustar0000000000000000pub mod parser; anes-0.1.6/benches/benchmarks/parser.rs010064400007650000024000000007571357023236000162630ustar0000000000000000use criterion::{black_box, criterion_group, Criterion}; use anes::parser::Parser; pub fn parser(c: &mut Criterion) { const XTERM_MOUSE: &str = "\x1B[<28;20;10;m"; let mut parser = Parser::default(); c.bench_function("advance and consume", |b| { let input = XTERM_MOUSE.as_bytes(); b.iter(|| { parser.advance(black_box(input), black_box(true)); while let Some(_) = parser.next() {} }) }); } criterion_group!(benches, parser); anes-0.1.6/Cargo.toml.orig010064400007650000024000000013741357024077400135700ustar0000000000000000[package] name = "anes" version = "0.1.6" authors = ["Robert Vojta "] edition = "2018" description = "ANSI Escape Sequences provider & parser" repository = "https://github.com/zrzka/anes-rs" documentation = "https://docs.rs/anes/" license = "MIT OR Apache-2.0" keywords = ["terminal", "ansi", "sequence", "code", "parser"] exclude = ["target", "Cargo.lock"] readme = "README.md" [lib] bench = false [badges] maintenance = { status = "actively-developed" } [package.metadata.docs.rs] all-features = true [features] default = [] parser = ["bitflags"] [dependencies] bitflags = { version = "1.2", optional = true } [dev-dependencies] criterion = "0.3" libc = "0.2.66" [[bench]] name = "bench_main" harness = false required-features = ["parser"] anes-0.1.6/Cargo.toml0000644000000024340000000000000100260ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "anes" version = "0.1.6" authors = ["Robert Vojta "] exclude = ["target", "Cargo.lock"] description = "ANSI Escape Sequences provider & parser" documentation = "https://docs.rs/anes/" readme = "README.md" keywords = ["terminal", "ansi", "sequence", "code", "parser"] license = "MIT OR Apache-2.0" repository = "https://github.com/zrzka/anes-rs" [package.metadata.docs.rs] all-features = true [lib] bench = false [[bench]] name = "bench_main" harness = false required-features = ["parser"] [dependencies.bitflags] version = "1.2" optional = true [dev-dependencies.criterion] version = "0.3" [dev-dependencies.libc] version = "0.2.66" [features] default = [] parser = ["bitflags"] [badges.maintenance] status = "actively-developed" anes-0.1.6/examples/execute.rs010064400007650000024000000005171356545316000145240ustar0000000000000000/// An example how to execute the ANSI escape sequence. use std::io::{Result, Write}; use anes::execute; fn main() -> Result<()> { let mut stdout = std::io::stdout(); execute!( &mut stdout, anes::SaveCursorPosition, anes::MoveCursorTo(10, 10), anes::RestoreCursorPosition )?; Ok(()) } anes-0.1.6/examples/parser.rs010064400007650000024000000050751357024077400143630ustar0000000000000000/// An example how to use the ANSI escape sequence parser. use std::io::{Read, Result, Write}; use anes::{ self, execute, parser::{KeyCode, Parser, Sequence}, queue, }; use libc::termios as Termios; const HELP: &str = r#"ANES parser example * Hit `Esc` to quit * Hit 'c' to ask for cursor position * Use your mouse or type anything "#; fn main() -> Result<()> { let mut w = std::io::stdout(); queue!( w, anes::SwitchBufferToAlternate, anes::HideCursor, anes::EnableMouseEvents )?; for line in HELP.split('\n') { queue!(w, line, anes::MoveCursorToNextLine(1))?; } w.flush()?; let saved_attributes = get_termios()?; let mut attributes = saved_attributes; make_raw(&mut attributes); set_termios(attributes)?; let mut stdin = std::io::stdin(); let mut stdin_buffer = [0u8; 1024]; let mut parser = Parser::default(); loop { if let Ok(size) = stdin.read(&mut stdin_buffer) { parser.advance(&stdin_buffer[..size], false); let mut break_outer_loop = false; while let Some(sequence) = parser.next() { match sequence { Sequence::Key(KeyCode::Esc, _) => { break_outer_loop = true; break; } Sequence::Key(KeyCode::Char('c'), _) => { execute!(w, anes::ReportCursorPosition)? } _ => execute!( w, anes::ClearLine::Left, anes::MoveCursorToColumn(1), format!("{:?}", sequence), )?, } } if break_outer_loop { break; } } } set_termios(saved_attributes)?; execute!( w, anes::DisableMouseEvents, anes::ShowCursor, anes::SwitchBufferToNormal )?; Ok(()) } // // RAW mode // fn get_termios() -> Result { unsafe { let mut termios = std::mem::zeroed(); if libc::tcgetattr(libc::STDIN_FILENO, &mut termios) != -1 { Ok(termios) } else { Err(std::io::Error::last_os_error()) } } } fn set_termios(termios: Termios) -> Result<()> { if unsafe { libc::tcsetattr(libc::STDIN_FILENO, libc::TCSANOW, &termios) } != -1 { Ok(()) } else { Err(std::io::Error::last_os_error()) } } fn make_raw(termios: &mut Termios) { unsafe { libc::cfmakeraw(termios) } } anes-0.1.6/examples/queue.rs010064400007650000024000000006511356545316000142050ustar0000000000000000/// An example how to queue & flush the ANSI escape sequence. use std::io::{Result, Write}; use anes::queue; fn main() -> Result<()> { let mut stdout = std::io::stdout(); queue!( &mut stdout, anes::SaveCursorPosition, anes::MoveCursorTo(10, 10) )?; queue!(&mut stdout, anes::RestoreCursorPosition,)?; // ANSI sequences are not executed until you flush it! stdout.flush() } anes-0.1.6/examples/sequence.rs010064400007650000024000000017611355624440500146740ustar0000000000000000/// An example how to create custom ANSI sequences. use anes::{csi, esc, sequence}; fn static_unit_struct() { sequence!( /// Documentation string is also supported. struct Foo => csi!("foo") ); assert_eq!(&format!("{}", Foo), "\x1B[foo"); } fn dynamic_struct() { sequence!( /// Documentation string is also supported. struct Foo(u16, u16) => |this, f| write!(f, esc!("{};{}"), this.0, this.1) ); assert_eq!(&format!("{}", Foo(5, 10)), "\x1B5;10"); } fn static_enum() { sequence!( /// Documentation string is also supported. enum Foo { /// Documentation string is also supported. Bar => esc!("bar"), /// Documentation string is also supported. Baz => csi!("baz"), } ); assert_eq!(&format!("{}", Foo::Bar), "\x1Bbar"); assert_eq!(&format!("{}", Foo::Baz), "\x1B[baz"); } fn main() { static_unit_struct(); dynamic_struct(); static_enum(); } anes-0.1.6/examples/stdout.rs010064400007650000024000000004571355624440500144070ustar0000000000000000/// An example how to use the ANSI escape sequence. use std::io::{Result, Write}; use anes; fn main() -> Result<()> { let mut stdout = std::io::stdout(); write!(stdout, "{}", anes::SaveCursorPosition)?; write!(stdout, "{}", anes::RestoreCursorPosition)?; stdout.flush()?; Ok(()) } anes-0.1.6/examples/string.rs010064400007650000024000000003121355624440500143610ustar0000000000000000//! An example how to retrieve the ANSI escape sequence as a `String`. use anes::SaveCursorPosition; fn main() { let string = format!("{}", SaveCursorPosition); assert_eq!(&string, "\x1B7"); } anes-0.1.6/README.md010064400007650000024000000061631357023236000121510ustar0000000000000000[![Stable Status][actions-stable-badge]][actions-link] [![Beta Status][actions-beta-badge]][actions-link] [![Nightly Status][actions-nightly-badge]][actions-link] [![crates.io][crates-badge]][crates-link] [![docs.rs][docs-badge]][docs-link] [![MIT][mit-license-badge]][mit-license-link] [![Apache 2.0][apache-license-badge]][apache-license-link] ![LOC][loc-badge] # ANSI Escape Sequences provider & parser A Rust library which provides an ANSI escape sequences (or codes, whatever you like more) and a parser allowing you to parse data from the STDIN (or `/dev/tty`) in the raw mode. ## Sequences provider ### Terminal support Not all ANSI escape sequences are supported by all terminals. You can use the [interactive-test](https://github.com/zrzka/anes-rs/tree/master/interactive-test) to test them. Checkout the repository and then: ```bash $ cd anes-rs $ cargo run --bin interactive-test ``` ### Examples
Click to show Cargo.toml. ```toml [dependencies] anes = "0.1" ```

An example how to retrieve the ANSI escape sequence as a `String`: ```rust use anes::SaveCursorPosition; fn main() { let string = format!("{}", SaveCursorPosition); assert_eq!(&string, "\x1B7"); } ``` An example how to use the ANSI escape sequence: ```rust use std::io::{Result, Write}; use anes::execute; fn main() -> Result<()> { let mut stdout = std::io::stdout(); execute!( &mut stdout, anes::SaveCursorPosition, anes::MoveCursorTo(10, 10), anes::RestoreCursorPosition )?; Ok(()) } ``` ## Sequences parser You have to enable `parser` feature in order to use the parser. It's disabled by default. ### Examples
Click to show Cargo.toml. ```toml [dependencies] anes = { version = "0.1", features = ["parser"] } ```

An example how to parse cursor position: ```rust use anes::parser::{Parser, Sequence}; let mut parser = Parser::default(); parser.advance(b"\x1B[20;10R", false); assert_eq!(Some(Sequence::CursorPosition(10, 20)), parser.next()); assert!(parser.next().is_none()); ``` ## License The ANES crate is dual-licensed under [Apache 2.0][apache-license-link] and [MIT][mit-license-link] terms. Copyrights in the ANES project are retained by their contributors. No copyright assignment is required to contribute to the ANES project. [actions-stable-badge]: https://github.com/zrzka/anes-rs/workflows/stable/badge.svg [actions-beta-badge]: https://github.com/zrzka/anes-rs/workflows/beta/badge.svg [actions-nightly-badge]: https://github.com/zrzka/anes-rs/workflows/nightly/badge.svg [actions-link]: https://github.com/zrzka/anes-rs/actions [crates-badge]: https://img.shields.io/crates/v/anes.svg [crates-link]: https://crates.io/crates/anes [docs-badge]: https://docs.rs/anes/badge.svg [docs-link]: https://docs.rs/anes [mit-license-badge]: https://img.shields.io/badge/license-MIT-blue.svg [mit-license-link]: ./LICENSE-MIT [apache-license-badge]: https://img.shields.io/badge/license-Apache2-blue.svg [apache-license-link]: /LICENSE-APACHE [loc-badge]: https://tokei.rs/b1/github/zrzka/anes-rs?category=code anes-0.1.6/src/lib.rs010064400007650000024000000050401357023467500126010ustar0000000000000000//! # ANSI Escape Sequences provider & parser //! //! ## Sequences provider //! //! The `anes` crate provides ANSI escape sequences you can use to control the terminal //! cursor (show, hide, ...), colors (foreground, background), display attributes (bold, ...) //! and many others. //! //! Every sequence implements the standard library [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html) //! trait. It means that these sequences can be used in macros like //! [`format!`](https://doc.rust-lang.org/std/macro.format.html) or //! [`write!`](https://doc.rust-lang.org/std/macro.write.html). //! //! Ask if you need more sequences or use the [`sequence!`](macro.sequence.html) macro to create //! your own sequences. //! //! //! ### Terminal Support //! //! Not all ANSI escape sequences are supported by all terminals. You can use the //! [interactive-test](https://github.com/zrzka/anes-rs/tree/master/interactive-test) to test them. //! //! ### Examples //! //! Retrieve the sequence as a `String`: //! //! ```rust //! use anes::SaveCursorPosition; //! //! let string = format!("{}", SaveCursorPosition); //! assert_eq!(&string, "\x1B7"); //! ``` //! //! Execute the sequence on the standard output: //! //! ```rust //! use std::io::{Result, Write}; //! //! use anes::execute; //! //! fn main() -> Result<()> { //! let mut stdout = std::io::stdout(); //! execute!(&mut stdout, anes::ResetAttributes) //! } //! ``` //! //! ## Sequences parser //! //! Parser isn't available with default features. You have to enable `parser` feature if you'd like to use it. //! You can learn more about this feature in the [`parser`](parser/index.html) module documentation. #![warn(rust_2018_idioms)] #![deny(unused_imports, unused_must_use)] // Keep it first to load all the macros before other modules. #[macro_use] mod macros; pub use self::sequences::{ attribute::{Attribute, ResetAttributes, SetAttribute}, buffer::{ ClearBuffer, ClearLine, ScrollBufferDown, ScrollBufferUp, SwitchBufferToAlternate, SwitchBufferToNormal, }, color::{Color, SetBackgroundColor, SetForegroundColor}, cursor::{ DisableCursorBlinking, EnableCursorBlinking, HideCursor, MoveCursorDown, MoveCursorLeft, MoveCursorRight, MoveCursorTo, MoveCursorToColumn, MoveCursorToNextLine, MoveCursorToPreviousLine, MoveCursorUp, ReportCursorPosition, RestoreCursorPosition, SaveCursorPosition, ShowCursor, }, terminal::{DisableMouseEvents, EnableMouseEvents, ResizeTextArea}, }; #[cfg(feature = "parser")] pub mod parser; mod sequences; anes-0.1.6/src/macros.rs010064400007650000024000000250331356545316000133170ustar0000000000000000/// Creates a control sequence. /// /// This macro prepends provided sequence with the control sequence introducer `ESC [` (`\x1B[`). /// /// # Examples /// /// ``` /// use anes::csi; /// /// assert_eq!(csi!("?1049h"), "\x1B[?1049h"); /// ``` #[macro_export] macro_rules! csi { ($($arg:expr),*) => { concat!("\x1B[", $($arg),*) }; } /// Creates an escape sequence. /// /// This macro prepends provided sequence with the `ESC` (`\x1B`) character. /// /// # Examples /// /// ``` /// use anes::esc; /// /// assert_eq!(esc!("7"), "\x1B7"); /// ``` #[macro_export] macro_rules! esc { ($($arg:expr),*) => { concat!("\x1B", $($arg),*) }; } /// Creates a select graphic rendition sequence. /// /// This macro prepends provided sequence with the `ESC[` (`\x1B[`) character and appends `m` character. /// /// Also known as Set Graphics Rendition on Linux. /// /// # Examples /// /// ``` /// use anes::sgr; /// /// assert_eq!(sgr!("0"), "\x1B[0m"); /// ``` #[macro_export] macro_rules! sgr { ($($arg:expr),*) => { concat!("\x1B[", $($arg),* , "m") }; } /// Creates an ANSI sequence. /// /// You can use this macro to create your own ANSI sequence. All `anes` sequences are /// created with this macro. /// /// # Examples /// /// An unit struct: /// /// ``` /// use anes::{esc, sequence}; /// /// sequence!( /// /// Saves the cursor position. /// struct SaveCursorPosition => esc!("7") /// ); /// /// assert_eq!(&format!("{}", SaveCursorPosition), "\x1B7"); /// ``` /// /// An enum: /// /// ``` /// use anes::{csi, sequence}; /// /// sequence!( /// /// Clears part of the buffer. /// enum ClearBuffer { /// /// Clears from the cursor position to end of the screen. /// Below => csi!("J"), /// /// Clears from the cursor position to beginning of the screen. /// Above => csi!("1J"), /// /// Clears the entire buffer. /// All => csi!("2J"), /// /// Clears the entire buffer and all saved lines in the scrollback buffer. /// SavedLines => csi!("3J"), /// } /// ); /// /// assert_eq!(&format!("{}", ClearBuffer::Below), "\x1B[J"); /// assert_eq!(&format!("{}", ClearBuffer::Above), "\x1B[1J"); /// assert_eq!(&format!("{}", ClearBuffer::All), "\x1B[2J"); /// assert_eq!(&format!("{}", ClearBuffer::SavedLines), "\x1B[3J"); /// ``` /// /// A struct: /// /// ``` /// use anes::{csi, sequence}; /// /// sequence!( /// /// Moves the cursor to the given location (column, row). /// /// /// /// # Notes /// /// /// /// Top/left cell is represented as `1, 1`. /// struct MoveCursorTo(u16, u16) => /// |this, f| write!(f, csi!("{};{}H"), this.0, this.1) /// ); /// /// assert_eq!(&format!("{}", MoveCursorTo(10, 5)), "\x1B[10;5H"); /// ``` #[macro_export] macro_rules! sequence { // Static unit struct ( $(#[$meta:meta])* struct $name:ident => $value:expr ) => { $(#[$meta])* #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] pub struct $name; impl ::std::fmt::Display for $name { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { write!(f, $value) } } }; // Static enum ( $(#[$meta:meta])* enum $name:ident { $( $(#[$variant_meta:meta])* $variant:ident => $variant_value:expr ),* $(,)? } ) => { $(#[$meta])* #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] pub enum $name { $( $(#[$variant_meta])* $variant, )* } impl ::std::fmt::Display for $name { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { write!(f, "{}", match self { $( $name::$variant => $variant_value, )* }) } } }; // Dynamic struct ( $(#[$meta:meta])* struct $type:ident( $($fields:ty),* $(,)? ) => $write:expr ) => { $(#[$meta])* #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] pub struct $type($(pub $fields),*); impl ::std::fmt::Display for $type { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { let write: &dyn Fn(&Self, &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result = &$write; write(self, f) } } }; } /// Queues ANSI escape sequence(s). /// /// What does queue mean exactly? All sequences are queued with the /// `write!($dst, "{}", $sequence)` macro without calling the /// [`flush`](https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.flush) method. /// /// Check the [`execute!`](macro.execute.html) macro if you'd like execute them /// immediately (call the `flush` method after all sequences were queued). /// /// # Examples /// /// ```no_run /// use std::io::{Result, Write}; /// /// use anes::queue; /// /// fn main() -> Result<()> { /// let mut stdout = std::io::stdout(); /// queue!( /// &mut stdout, /// anes::SaveCursorPosition, /// anes::MoveCursorTo(10, 10) /// )?; /// /// queue!(&mut stdout, anes::RestoreCursorPosition,)?; /// /// // ANSI sequences are not executed until you flush it! /// stdout.flush() /// } /// ``` #[macro_export] macro_rules! queue { ($dst:expr, $($sequence:expr),* $(,)?) => {{ let mut error = None; $( if let Err(e) = write!($dst, "{}", $sequence) { error = Some(e); } )* if let Some(error) = error { Err(error) } else { Ok(()) } }} } /// Executes ANSI escape sequence(s). /// /// What does execute mean exactly? All sequences are queued with the /// `write!($dst, "{}", $sequence)` macro and then the /// [`flush`](https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.flush) method /// is called. /// /// Check the [`queue!`](macro.queue.html) macro if you'd like queue sequences /// and execute them later. /// /// ```no_run /// use std::io::{Result, Write}; /// /// use anes::execute; /// /// fn main() -> Result<()> { /// let mut stdout = std::io::stdout(); /// execute!( /// &mut stdout, /// anes::SaveCursorPosition, /// anes::MoveCursorTo(10, 10), /// anes::RestoreCursorPosition /// )?; /// Ok(()) /// } /// ``` #[macro_export] macro_rules! execute { ($dst:expr, $($sequence:expr),* $(,)?) => {{ if let Err(e) = $crate::queue!($dst, $($sequence),*) { Err(e) } else { $dst.flush() } }} } #[cfg(test)] macro_rules! test_sequences { ( $( $name:ident( $($left:expr => $right:expr),* $(,)? ) ),* $(,)? ) => { #[cfg(test)] mod tests { use super::*; $( #[test] fn $name() { $( assert_eq!(&format!("{}", $left), $right); )* } )* } } } #[cfg(test)] mod tests { use std::io::{Error, ErrorKind, Write}; #[test] fn csi() { assert_eq!(csi!("foo"), "\x1B[foo"); } #[test] fn esc() { assert_eq!(esc!("bar"), "\x1Bbar"); } #[test] fn sgr() { assert_eq!(sgr!("bar"), "\x1B[barm"); } #[test] fn static_struct_sequence() { sequence!( struct TestSeq => csi!("foo") ); assert_eq!(&format!("{}", TestSeq), "\x1B[foo"); } #[test] fn static_enum_sequence() { sequence!( enum TestSeq { Foo => csi!("foo"), Bar => esc!("bar"), } ); assert_eq!(&format!("{}", TestSeq::Foo), "\x1B[foo"); assert_eq!(&format!("{}", TestSeq::Bar), "\x1Bbar"); } #[test] fn dynamic_struct_sequence() { sequence!( struct TestSeq(u16) => |this, f| write!(f, csi!("foo{}bar"), this.0) ); assert_eq!(&format!("{}", TestSeq(10)), "\x1B[foo10bar"); } #[test] fn queue_allows_trailing_comma() { let mut writer = Writer::default(); assert!(queue!(&mut writer, "foo",).is_ok()); assert_eq!(&writer.buffer, "foo"); } #[test] fn queue_writes_single_sequence() { let mut writer = Writer::default(); assert!(queue!(&mut writer, "foo").is_ok()); assert_eq!(&writer.buffer, "foo"); } #[test] fn queue_writes_multiple_sequences() { let mut writer = Writer::default(); assert!(queue!(&mut writer, "foo", "bar", "baz").is_ok()); assert_eq!(&writer.buffer, "foobarbaz"); } #[test] fn queue_does_not_flush() { let mut writer = Writer::default(); assert!(queue!(&mut writer, "foo").is_ok()); assert!(!writer.flushed); assert!(writer.flushed_buffer.is_empty()); } #[test] fn execute_allows_trailing_comma() { let mut writer = Writer::default(); assert!(execute!(&mut writer, "foo",).is_ok()); assert_eq!(&writer.flushed_buffer, "foo"); } #[test] fn execute_writes_single_sequence() { let mut writer = Writer::default(); assert!(execute!(&mut writer, "foo").is_ok()); assert_eq!(&writer.flushed_buffer, "foo"); } #[test] fn execute_writes_multiple_sequences() { let mut writer = Writer::default(); assert!(execute!(&mut writer, "foo", "bar", "baz").is_ok()); assert_eq!(&writer.flushed_buffer, "foobarbaz"); } #[test] fn execute_does_flush() { let mut writer = Writer::default(); assert!(execute!(&mut writer, "foo").is_ok()); assert!(writer.flushed); assert_eq!(&writer.flushed_buffer, "foo"); assert!(writer.buffer.is_empty()); } #[derive(Default)] struct Writer { buffer: String, flushed_buffer: String, flushed: bool, } impl Write for Writer { fn write(&mut self, buf: &[u8]) -> Result { let s = std::str::from_utf8(buf).map_err(|_| ErrorKind::InvalidData)?; self.buffer.push_str(s); Ok(s.len()) } fn flush(&mut self) -> Result<(), Error> { self.flushed_buffer = self.buffer.clone(); self.buffer = String::new(); self.flushed = true; Ok(()) } } } anes-0.1.6/src/parser.rs010064400007650000024000000177571357023236000133360ustar0000000000000000//! An ANSI escape sequence parser module. //! //! **This module is not available with default features. You have to enable `parser` feature //! if you'd like to use it.** //! //! # Parser //! //! The ANSI escape sequence parser parses input data in two steps: //! //! * transforms input data into valid characters, generic csi & escape sequences, throws away invalid data, //! * give them meaning, throws away sequences without known meaning. //! //! ## First step //! //! State machine implementation for the first step is inspired by the //! [A parser for DEC’s ANSI-compatible video terminals](https://vt100.net/emu/dec_ansi_parser) article //! and the [vte](https://crates.io/crates/vte) crate. The goal of this step is to transform an input //! data into characters, generic csi & escape sequences, validate them and throw away malformed input. //! //! An example of valid csi sequence: `b"\x1B[20;10R"`. Output of the first step will be: //! //! * valid csi sequence //! * with two parameters `[20, 10]` //! * and the final character `R`. //! //! ## Second step //! //! An input of this step is output of the first one. We know that the final character `R` represents //! cursor position and two parameters should be provided. They were provided, we can give it a //! meaning and return `Sequence::CursorPosition(10, 20)`. //! //! All sequences without known meaning are discarded. //! //! ## Implementation //! //! Both steps are considered as an implementation detail and there's no plan to make them //! publicly available. //! //! The `parser` module provides the [`Parser`](struct.Parser.html) structure you can feed with //! the [`advance`](struct.Parser.html#method.advance) method. It also implements the standard //! library `Iterator` trait which allows you to consume valid sequences with //! known meaning via the `next()` method. Check the [`Sequence`](enum.Sequence.html) enum to learn //! what this module can parse. use std::collections::VecDeque; use engine::{Engine, Provide}; pub use types::{KeyCode, KeyModifiers, Mouse, MouseButton, Sequence}; mod engine; mod parsers; pub(crate) mod types; /// An ANSI escape sequence parser. /// /// `Parser` implements the `Iterator` trait, thus you can use the /// `next()` method to consume all valid sequences with known meaning. /// /// # Examples /// /// Parse cursor position: /// /// ``` /// use anes::parser::{Parser, Sequence}; /// /// let mut parser = Parser::default(); /// parser.advance(b"\x1B[20;10R", false); /// /// assert_eq!(Some(Sequence::CursorPosition(10, 20)), parser.next()); /// assert!(parser.next().is_none()); /// ``` /// /// Parse keyboard event: /// /// ``` /// use anes::parser::{KeyCode, KeyModifiers, Parser, Sequence}; /// /// let mut parser = Parser::default(); /// parser.advance("𐌼a".as_bytes(), false); /// /// assert_eq!(Some(Sequence::Key(KeyCode::Char('𐌼'), KeyModifiers::empty())), parser.next()); /// assert_eq!(Some(Sequence::Key(KeyCode::Char('a'), KeyModifiers::empty())), parser.next()); /// assert!(parser.next().is_none()); /// ``` #[derive(Default)] pub struct Parser { engine: Engine, provider: SequenceProvider, } impl Parser { /// Advances parser state machine with additional input data. /// /// # Arguments /// /// * `buffer` - input data (stdin in raw mode, etc.) /// * `more` - more input data available right now /// /// It's crucial to provide correct `more` value in order to receive `KeyCode::Esc` events /// as soon as possible. /// /// # Examples /// /// Esc key: /// /// ``` /// use anes::parser::{KeyCode, KeyModifiers, Parser, Sequence}; /// /// let mut parser = Parser::default(); /// // User pressed Esc key & nothing else which means that there's no additional input available /// // aka no possible escape sequence = `KeyCode::Esc` dispatched. /// parser.advance(&[0x1b], false); /// /// assert_eq!(Some(Sequence::Key(KeyCode::Esc, KeyModifiers::empty())), parser.next()); /// assert!(parser.next().is_none()); /// ``` /// /// Possible escape sequence: /// /// ``` /// use anes::parser::{KeyCode, KeyModifiers, Parser, Sequence}; /// /// let mut parser = Parser::default(); /// // User pressed F1 = b"\x1BOP" /// /// // Every escape sequence starts with Esc (0x1b). There's more input available /// // aka possible escape sequence = `KeyCode::Esc` isn't dispatched even when the parser /// // doesn't know rest of the sequence. /// parser.advance(&[0x1b], true); /// assert!(parser.next().is_none()); /// /// // Advance parser with rest of the sequence /// parser.advance(&[b'O', b'P'], false); /// assert_eq!(Some(Sequence::Key(KeyCode::F(1), KeyModifiers::empty())), parser.next()); /// assert!(parser.next().is_none()); /// ``` pub fn advance(&mut self, buffer: &[u8], more: bool) { let len = buffer.len(); for (idx, byte) in buffer.iter().enumerate() { self.engine .advance(&mut self.provider, *byte, idx < len - 1 || more); } } } impl Iterator for Parser { type Item = Sequence; fn next(&mut self) -> Option { self.provider.next() } } #[derive(Default)] struct SequenceProvider { esc_o: bool, seqs: VecDeque, } impl Iterator for SequenceProvider { type Item = Sequence; fn next(&mut self) -> Option { self.seqs.pop_front() } } impl Provide for SequenceProvider { fn provide_char(&mut self, ch: char) { // eprintln!("dispatch_char: {}", ch); if let Some(seq) = parsers::parse_char(ch, self.esc_o) { self.seqs.push_back(seq); } self.esc_o = false; } fn provide_esc_sequence(&mut self, ch: char) { if ch == 'O' { // Exception // // Esc O - dispatched as an escape sequence followed by single character (P-S) representing // F1-F4 keys. We store Esc O flag only which is then used in the dispatch_char method. self.esc_o = true; } else { self.esc_o = false; if let Some(seq) = parsers::parse_esc_sequence(ch) { self.seqs.push_back(seq); } } } fn provide_csi_sequence(&mut self, parameters: &[u64], ignored_count: usize, ch: char) { if let Some(seq) = parsers::parse_csi_sequence(parameters, ignored_count, ch) { self.seqs.push_back(seq); } self.esc_o = false; } } #[cfg(test)] mod tests { use super::Parser; #[test] fn dispatch_char() { let mut parser = Parser::default(); parser.advance(&[b'a'], false); assert!(parser.next().is_some()); } #[test] fn dispatch_esc_sequence() { let mut parser = Parser::default(); parser.advance(&[b'\x1B'], true); assert!(parser.next().is_none()); parser.advance(&[b'a'], false); assert!(parser.next().is_some()); } #[test] fn does_not_dispatch_esc_sequence_with_upper_case_o() { let mut parser = Parser::default(); parser.advance(&[b'\x1B'], true); assert!(parser.next().is_none()); parser.advance(&[b'O'], true); assert!(parser.next().is_none()); } #[test] fn dispatch_esc_with_upper_case_o_followed_by_char_as_single_sequence() { let mut parser = Parser::default(); parser.advance(&[b'\x1B'], true); assert!(parser.next().is_none()); parser.advance(&[b'O'], true); assert!(parser.next().is_none()); parser.advance(&[b'P'], false); assert!(parser.next().is_some()); assert!(parser.next().is_none()); } #[test] fn dispatch_csi_sequence() { let mut parser = Parser::default(); parser.advance(&[b'\x1B'], true); assert!(parser.next().is_none()); parser.advance(&[b'['], true); assert!(parser.next().is_none()); parser.advance(&[b'D'], false); assert!(parser.next().is_some()); } } anes-0.1.6/src/parser/engine.rs010064400007650000024000000471321357023236000145710ustar0000000000000000// // https://vt100.net/emu/dec_ansi_parser // // The parser is heavily inspired by the vte (https://crates.io/crates/vte) crate. // Tried to use this crate, but it doesn't work for opposite way (terminal -> sequence), // because there're couple of exceptions we have to handle and it doesn't make much // sense to add them to the vte crate. An example is Esc key where we need to know if // there's additional input available or not and then the decision is made if the // Esc char is dispatched immediately (user hits just Esc key) or if it's an escape/csi/... // sequence. // const MAX_PARAMETERS: usize = 30; const DEFAULT_PARAMETER_VALUE: u64 = 0; const MAX_UTF8_CODE_POINTS: usize = 4; /// A parser engine state. /// /// All these variant names come from the /// [A parser for DEC’s ANSI-compatible video terminals](https://vt100.net/emu/dec_ansi_parser) /// description. #[derive(Copy, Clone, Debug, Eq, PartialEq)] enum State { /// Initial state. Ground, /// Escape sequence started. /// /// `Esc` received with a flag that there's more data available. Escape, /// Escape sequence and we're collecting intermediates. /// /// # Notes /// /// This implementation doesn't collect intermediates. It just handles the state /// to distinguish between (im)proper sequences. EscapeIntermediate, /// CSI sequence started. /// /// `Esc` followed by the `[` received. CsiEntry, /// CSI sequence should be consumed, but not dispatched. CsiIgnore, /// CSI sequence and we're collecting parameters. CsiParameter, /// CSI sequence and we're collecting intermediates. /// /// # Notes /// /// This implementation doesn't collect intermediates. It just handles the state /// to distinguish between (im)proper sequences. CsiIntermediate, /// Possible UTF-8 sequence and we're collecting UTF-8 code points. Utf8, } pub(crate) trait Provide { fn provide_char(&mut self, ch: char); fn provide_esc_sequence(&mut self, ch: char); fn provide_csi_sequence(&mut self, parameters: &[u64], ignored_count: usize, ch: char); } pub(crate) struct Engine { parameters: [u64; MAX_PARAMETERS], parameters_count: usize, parameter: u64, ignored_parameters_count: usize, state: State, utf8_points: [u8; MAX_UTF8_CODE_POINTS], utf8_points_count: usize, utf8_points_expected_count: usize, } impl Default for Engine { fn default() -> Self { Engine { parameters: [DEFAULT_PARAMETER_VALUE; MAX_PARAMETERS], parameters_count: 0, parameter: DEFAULT_PARAMETER_VALUE, ignored_parameters_count: 0, state: State::Ground, utf8_points: [0; MAX_UTF8_CODE_POINTS], utf8_points_count: 0, utf8_points_expected_count: 0, } } } impl Engine { fn set_state(&mut self, state: State) { if let State::Ground = state { self.parameters_count = 0; self.parameter = DEFAULT_PARAMETER_VALUE; self.ignored_parameters_count = 0; self.utf8_points_count = 0; self.utf8_points_expected_count = 0; } self.state = state; } fn store_parameter(&mut self) { if self.parameters_count < MAX_PARAMETERS { self.parameters[self.parameters_count] = self.parameter; self.parameters_count += 1; } else { self.ignored_parameters_count += 1; } self.parameter = DEFAULT_PARAMETER_VALUE; } fn handle_possible_esc(&mut self, provider: &mut dyn Provide, byte: u8, more: bool) -> bool { if byte != 0x1B { return false; } match (self.state, more) { // More input means possible Esc sequence, just switch state and wait (State::Ground, true) => self.set_state(State::Escape), // No more input means Esc key, dispatch it (State::Ground, false) => provider.provide_char('\x1B'), // More input means possible Esc sequence, dispatch the previous Esc char (State::Escape, true) => provider.provide_char('\x1B'), // No more input means Esc key, dispatch the previous & current Esc char (State::Escape, false) => { provider.provide_char('\x1B'); provider.provide_char('\x1B'); self.set_state(State::Ground); } // Discard any state // More input means possible Esc sequence (_, true) => self.set_state(State::Escape), // Discard any state // No more input means Esc key, dispatch it (_, false) => { provider.provide_char('\x1B'); self.set_state(State::Ground); } } true } fn handle_possible_utf8_code_points(&mut self, provider: &mut dyn Provide, byte: u8) -> bool { if byte & 0b1000_0000 == 0b0000_0000 { provider.provide_char(byte as char); true } else if byte & 0b1110_0000 == 0b1100_0000 { self.utf8_points_count = 1; self.utf8_points[0] = byte; self.utf8_points_expected_count = 2; self.set_state(State::Utf8); true } else if byte & 0b1111_0000 == 0b1110_0000 { self.utf8_points_count = 1; self.utf8_points[0] = byte; self.utf8_points_expected_count = 3; self.set_state(State::Utf8); true } else if byte & 0b1111_1000 == 0b1111_0000 { self.utf8_points_count = 1; self.utf8_points[0] = byte; self.utf8_points_expected_count = 4; self.set_state(State::Utf8); true } else { false } } fn advance_ground_state(&mut self, provider: &mut dyn Provide, byte: u8) { if self.handle_possible_utf8_code_points(provider, byte) { return; } match byte { 0x1B => unreachable!(), // Execute 0x00..=0x17 | 0x19 | 0x1C..=0x1F => provider.provide_char(byte as char), // Print 0x20..=0x7F => provider.provide_char(byte as char), _ => {} }; } fn advance_escape_state(&mut self, provider: &mut dyn Provide, byte: u8) { match byte { 0x1B => unreachable!(), // Intermediate bytes to collect 0x20..=0x2F => { self.set_state(State::EscapeIntermediate); } // Escape followed by '[' (0x5B) // -> CSI sequence start 0x5B => self.set_state(State::CsiEntry), // Escape sequence final character 0x30..=0x4F | 0x51..=0x57 | 0x59 | 0x5A | 0x5C | 0x60..=0x7E => { provider.provide_esc_sequence(byte as char); self.set_state(State::Ground); } // Execute 0x00..=0x17 | 0x19 | 0x1C..=0x1F => provider.provide_char(byte as char), // TODO Does it mean we should ignore the whole sequence? // Ignore 0x7F => {} // Other bytes are considered as invalid -> cancel whatever we have _ => self.set_state(State::Ground), }; } fn advance_escape_intermediate_state(&mut self, provider: &mut dyn Provide, byte: u8) { match byte { 0x1B => unreachable!(), // Intermediate bytes to collect 0x20..=0x2F => {} // Escape followed by '[' (0x5B) // -> CSI sequence start 0x5B => self.set_state(State::CsiEntry), // Escape sequence final character 0x30..=0x5A | 0x5C..=0x7E => { provider.provide_esc_sequence(byte as char); self.set_state(State::Ground); } // Execute 0x00..=0x17 | 0x19 | 0x1C..=0x1F => provider.provide_char(byte as char), // TODO Does it mean we should ignore the whole sequence? // Ignore 0x7F => {} // Other bytes are considered as invalid -> cancel whatever we have _ => self.set_state(State::Ground), }; } fn advance_csi_entry_state(&mut self, provider: &mut dyn Provide, byte: u8) { match byte { 0x1B => unreachable!(), // Semicolon = parameter delimiter 0x3B => { self.store_parameter(); self.set_state(State::CsiParameter); } // '0' ..= '9' = parameter value 0x30..=0x39 => { self.parameter = (byte as u64) - 0x30; self.set_state(State::CsiParameter); } 0x3A => self.set_state(State::CsiIgnore), // CSI sequence final character // -> dispatch CSI sequence 0x40..=0x7E => { provider.provide_csi_sequence( &self.parameters[..self.parameters_count], self.ignored_parameters_count, byte as char, ); self.set_state(State::Ground); } // Execute 0x00..=0x17 | 0x19 | 0x1C..=0x1F => provider.provide_char(byte as char), // TODO Does it mean we should ignore the whole sequence? // Ignore 0x7F => {} // Collect rest as parameters _ => { self.parameter = byte as u64; self.store_parameter(); } }; } fn advance_csi_ignore_state(&mut self, provider: &mut dyn Provide, byte: u8) { match byte { 0x1B => unreachable!(), // Execute 0x00..=0x17 | 0x19 | 0x1C..=0x1F => provider.provide_char(byte as char), // TODO Does it mean we should ignore the whole sequence? // Ignore 0x20..=0x3F | 0x7F => {} 0x40..=0x7E => self.set_state(State::Ground), // Other bytes are considered as invalid -> cancel whatever we have _ => self.set_state(State::Ground), }; } fn advance_csi_parameter_state(&mut self, provider: &mut dyn Provide, byte: u8) { match byte { 0x1B => unreachable!(), // '0' ..= '9' = parameter value 0x30..=0x39 => { self.parameter = self.parameter.saturating_mul(10); self.parameter = self.parameter.saturating_add((byte as u64) - 0x30); } // Semicolon = parameter delimiter 0x3B => self.store_parameter(), // CSI sequence final character // -> dispatch CSI sequence 0x40..=0x7E => { self.store_parameter(); provider.provide_csi_sequence( &self.parameters[..self.parameters_count], self.ignored_parameters_count, byte as char, ); self.set_state(State::Ground); } // Intermediates to collect 0x20..=0x2F => { self.store_parameter(); self.set_state(State::CsiIntermediate); } // Ignore 0x3A | 0x3C..=0x3F => self.set_state(State::CsiIgnore), // Execute 0x00..=0x17 | 0x19 | 0x1C..=0x1F => provider.provide_char(byte as char), // TODO Does it mean we should ignore the whole sequence? // Ignore 0x7F => {} // Other bytes are considered as invalid -> cancel whatever we have _ => self.set_state(State::Ground), }; } fn advance_csi_intermediate_state(&mut self, provider: &mut dyn Provide, byte: u8) { match byte { 0x1B => unreachable!(), // Intermediates to collect 0x20..=0x2F => {} // CSI sequence final character // -> dispatch CSI sequence 0x40..=0x7E => { provider.provide_csi_sequence( &self.parameters[..self.parameters_count], self.ignored_parameters_count, byte as char, ); self.set_state(State::Ground); } // Execute 0x00..=0x17 | 0x19 | 0x1C..=0x1F => provider.provide_char(byte as char), // TODO Does it mean we should ignore the whole sequence? // Ignore 0x7F => {} // Other bytes are considered as invalid -> cancel whatever we have _ => self.set_state(State::Ground), } } fn advance_utf8_state(&mut self, provider: &mut dyn Provide, byte: u8) { if byte & 0b1100_0000 != 0b1000_0000 { self.set_state(State::Ground); return; } self.utf8_points[self.utf8_points_count] = byte; self.utf8_points_count += 1; if self.utf8_points_count == self.utf8_points_expected_count { if let Some(ch) = std::str::from_utf8(&self.utf8_points[..self.utf8_points_count]) .ok() .and_then(|s| s.chars().next()) { provider.provide_char(ch); } self.set_state(State::Ground); } } pub(crate) fn advance(&mut self, provider: &mut dyn Provide, byte: u8, more: bool) { // eprintln!("advance: {:?} {} {}", self.state, byte, more); if self.handle_possible_esc(provider, byte, more) { return; } match self.state { State::Ground => self.advance_ground_state(provider, byte), State::Escape => self.advance_escape_state(provider, byte), State::EscapeIntermediate => self.advance_escape_intermediate_state(provider, byte), State::CsiEntry => self.advance_csi_entry_state(provider, byte), State::CsiIgnore => self.advance_csi_ignore_state(provider, byte), State::CsiParameter => self.advance_csi_parameter_state(provider, byte), State::CsiIntermediate => self.advance_csi_intermediate_state(provider, byte), State::Utf8 => self.advance_utf8_state(provider, byte), }; } } #[cfg(test)] mod tests { use super::*; #[test] fn esc_char() { let mut engine = Engine::default(); let mut provider = CharProvider::default(); // No more input means that the Esc character should be dispatched immediately engine.advance(&mut provider, 0x1B, false); assert_eq!(provider.chars, &['\x1B']); // There's more input so the machine should wait before dispatching Esc character engine.advance(&mut provider, 0x1B, true); assert_eq!(provider.chars, &['\x1B']); // Another Esc character, but no more input, machine should dispatch the postponed Esc // character and the new one too. engine.advance(&mut provider, 0x1B, false); assert_eq!(provider.chars, &['\x1B', '\x1B', '\x1B']); } #[test] fn esc_without_intermediates() { let mut engine = Engine::default(); let mut provider = EscProvider::default(); let input = b"\x1B0\x1B~"; advance(&mut engine, &mut provider, input, false); assert_eq!(provider.chars.len(), 2); assert_eq!(provider.chars[0], '0'); assert_eq!(provider.chars[1], '~'); } #[test] fn csi_without_parameters() { let mut engine = Engine::default(); let mut provider = CsiProvider::default(); let input = b"\x1B\x5Bm"; advance(&mut engine, &mut provider, input, false); assert_eq!(provider.parameters.len(), 1); assert_eq!(provider.parameters[0], &[]); assert_eq!(provider.chars.len(), 1); assert_eq!(provider.chars[0], 'm'); } #[test] fn csi_with_two_default_parameters() { let mut engine = Engine::default(); let mut provider = CsiProvider::default(); let input = b"\x1B\x5B;m"; advance(&mut engine, &mut provider, input, false); assert_eq!(provider.parameters.len(), 1); assert_eq!( provider.parameters[0], &[DEFAULT_PARAMETER_VALUE, DEFAULT_PARAMETER_VALUE] ); assert_eq!(provider.chars.len(), 1); assert_eq!(provider.chars[0], 'm'); } #[test] fn csi_with_trailing_semicolon() { let mut engine = Engine::default(); let mut provider = CsiProvider::default(); let input = b"\x1B\x5B123;m"; advance(&mut engine, &mut provider, input, false); assert_eq!(provider.parameters.len(), 1); assert_eq!(provider.parameters[0], &[123, DEFAULT_PARAMETER_VALUE]); assert_eq!(provider.chars.len(), 1); assert_eq!(provider.chars[0], 'm'); } #[test] fn csi_max_parameters() { let mut engine = Engine::default(); let mut provider = CsiProvider::default(); let input = b"\x1B\x5B1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;16;17;18;19;20;21;22;23;24;25;26;27;28;29;30m"; advance(&mut engine, &mut provider, input, false); assert_eq!(provider.parameters.len(), 1); assert_eq!(provider.parameters[0].len(), MAX_PARAMETERS); assert_eq!( provider.parameters[0], &[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 ] ); assert_eq!(provider.chars.len(), 1); assert_eq!(provider.chars[0], 'm'); } #[test] fn test_parse_utf8_character() { let mut engine = Engine::default(); let mut provider = CharProvider::default(); advance(&mut engine, &mut provider, &['a' as u8], false); assert_eq!(provider.chars.len(), 1); assert_eq!(provider.chars[0], 'a'); advance(&mut engine, &mut provider, &[0xC3, 0xB1], false); assert_eq!(provider.chars.len(), 2); assert_eq!(provider.chars[1], 'ñ'); advance(&mut engine, &mut provider, &[0xE2, 0x81, 0xA1], false); assert_eq!(provider.chars.len(), 3); assert_eq!(provider.chars[2], '\u{2061}'); advance(&mut engine, &mut provider, &[0xF0, 0x90, 0x8C, 0xBC], false); assert_eq!(provider.chars.len(), 4); assert_eq!(provider.chars[3], '𐌼'); } fn advance(engine: &mut Engine, provider: &mut dyn Provide, bytes: &[u8], more: bool) { let len = bytes.len(); for (i, byte) in bytes.iter().enumerate() { engine.advance(provider, *byte, i < len - 1 || more); } } #[derive(Default)] struct CharProvider { chars: Vec, } impl Provide for CharProvider { fn provide_char(&mut self, ch: char) { self.chars.push(ch); } fn provide_esc_sequence(&mut self, _ch: char) {} fn provide_csi_sequence(&mut self, _parameters: &[u64], _ignored_count: usize, _ch: char) {} } #[derive(Default)] struct CsiProvider { parameters: Vec>, chars: Vec, } impl Provide for CsiProvider { fn provide_char(&mut self, _ch: char) {} fn provide_esc_sequence(&mut self, _ch: char) {} fn provide_csi_sequence(&mut self, parameters: &[u64], _ignored_count: usize, ch: char) { self.parameters.push(parameters.to_vec()); self.chars.push(ch); } } #[derive(Default)] struct EscProvider { chars: Vec, } impl Provide for EscProvider { fn provide_char(&mut self, _ch: char) {} fn provide_esc_sequence(&mut self, ch: char) { self.chars.push(ch); } fn provide_csi_sequence(&mut self, _parameters: &[u64], _ignored_count: usize, _ch: char) {} } } anes-0.1.6/src/parser/parsers.rs010064400007650000024000000170571357023236000150060ustar0000000000000000use super::types::{KeyCode, KeyModifiers, Mouse, MouseButton, Sequence}; pub(crate) fn parse_char(ch: char, esc_o: bool) -> Option { if esc_o { return match ch { 'P'..='S' => Some(Sequence::Key( KeyCode::F(ch as u8 - b'P' + 1), KeyModifiers::empty(), )), _ => None, }; } let code = match ch { '\r' | '\n' => KeyCode::Enter, '\t' => KeyCode::Tab, '\x7F' => KeyCode::BackTab, '\x1B' => KeyCode::Esc, '\0' => KeyCode::Null, _ => KeyCode::Char(ch), }; Some(Sequence::Key(code, KeyModifiers::empty())) } pub(crate) fn parse_esc_sequence(ch: char) -> Option { // EscO[P-S] is handled in the Performer, see parse_char & esc_o argument // No need to handle other cases here? It's just Alt+$char Some(Sequence::Key(KeyCode::Char(ch), KeyModifiers::ALT)) } pub(crate) fn parse_csi_sequence( parameters: &[u64], _ignored_count: usize, ch: char, ) -> Option { match ch { 'A' => Some(Sequence::Key( KeyCode::Up, parse_csi_arrow_key_modifiers(parameters.first().cloned()), )), 'B' => Some(Sequence::Key( KeyCode::Down, parse_csi_arrow_key_modifiers(parameters.first().cloned()), )), 'C' => Some(Sequence::Key( KeyCode::Right, parse_csi_arrow_key_modifiers(parameters.first().cloned()), )), 'D' => Some(Sequence::Key( KeyCode::Left, parse_csi_arrow_key_modifiers(parameters.first().cloned()), )), 'H' => Some(Sequence::Key(KeyCode::Home, KeyModifiers::empty())), 'F' => Some(Sequence::Key(KeyCode::End, KeyModifiers::empty())), 'Z' => Some(Sequence::Key(KeyCode::BackTab, KeyModifiers::empty())), 'R' => parse_csi_cursor_position(parameters), 'm' => parse_csi_xterm_mouse(parameters, ch), 'M' if parameters.first() == Some(&0x3C) => parse_csi_xterm_mouse(parameters, ch), 'M' => parse_csi_rxvt_mouse(parameters), '~' => parse_csi_tilde_key_code(parameters), _ => None, } } fn parse_csi_arrow_key_modifiers(parameter: Option) -> KeyModifiers { parse_key_modifiers(parameter.map(|x| x.saturating_sub(48))) } fn parse_key_modifiers(parameter: Option) -> KeyModifiers { if let Some(parameter) = parameter { match parameter { 2 => KeyModifiers::SHIFT, 3 => KeyModifiers::ALT, 4 => KeyModifiers::SHIFT | KeyModifiers::ALT, 5 => KeyModifiers::CONTROL, 6 => KeyModifiers::SHIFT | KeyModifiers::CONTROL, 7 => KeyModifiers::ALT | KeyModifiers::CONTROL, 8 => KeyModifiers::SHIFT | KeyModifiers::ALT | KeyModifiers::CONTROL, 9 => KeyModifiers::META, 10 => KeyModifiers::META | KeyModifiers::SHIFT, 11 => KeyModifiers::META | KeyModifiers::ALT, 12 => KeyModifiers::META | KeyModifiers::SHIFT | KeyModifiers::ALT, 13 => KeyModifiers::META | KeyModifiers::CONTROL, 14 => KeyModifiers::META | KeyModifiers::SHIFT | KeyModifiers::CONTROL, 15 => KeyModifiers::META | KeyModifiers::ALT | KeyModifiers::CONTROL, 16 => { KeyModifiers::META | KeyModifiers::SHIFT | KeyModifiers::ALT | KeyModifiers::CONTROL } _ => KeyModifiers::empty(), } } else { KeyModifiers::empty() } } fn parse_csi_tilde_key_code(parameters: &[u64]) -> Option { if parameters.is_empty() { return None; } let modifiers = parse_key_modifiers(parameters.get(1).cloned()); let code = match parameters[0] { 1 | 7 => KeyCode::Home, 2 => KeyCode::Insert, 3 => KeyCode::Delete, 4 | 8 => KeyCode::End, 5 => KeyCode::PageUp, 6 => KeyCode::PageDown, p @ 11..=15 => KeyCode::F(p as u8 - 10), p @ 17..=21 => KeyCode::F(p as u8 - 11), p @ 23..=24 => KeyCode::F(p as u8 - 12), _ => return None, }; Some(Sequence::Key(code, modifiers)) } fn parse_csi_cursor_position(parameters: &[u64]) -> Option { // ESC [ Cy ; Cx R if parameters.len() < 2 { return None; } let y = parameters[0] as u16; let x = parameters[1] as u16; Some(Sequence::CursorPosition(x, y)) } fn parse_csi_xterm_mouse(parameters: &[u64], ch: char) -> Option { // ESC [ < Cb ; Cx ; Cy (;) (M or m) if parameters.len() < 4 { return None; } let cb = parameters[1] as u8; let cx = parameters[2] as u16; let cy = parameters[3] as u16; let up = match ch { 'm' => true, 'M' => false, _ => return None, }; let mut modifiers = KeyModifiers::empty(); if cb & 0b0000_0100 == 0b0000_0100 { modifiers |= KeyModifiers::SHIFT; } if cb & 0b0000_1000 == 0b0000_1000 { modifiers |= KeyModifiers::ALT; } if cb & 0b0001_0000 == 0b0001_0000 { modifiers |= KeyModifiers::CONTROL; } let mouse = if cb & 0b0100_0000 == 0b0100_0000 { if cb & 0b0000_0001 == 0b0000_0001 { Mouse::ScrollDown(cx, cy) } else { Mouse::ScrollUp(cx, cy) } } else { let drag = cb & 0b0010_0000 == 0b0010_0000; match (cb & 0b0000_0011, up, drag) { (0, true, _) => Mouse::Up(MouseButton::Left, cx, cy), (0, false, false) => Mouse::Down(MouseButton::Left, cx, cy), (0, false, true) => Mouse::Drag(MouseButton::Left, cx, cy), (1, true, _) => Mouse::Up(MouseButton::Middle, cx, cy), (1, false, false) => Mouse::Down(MouseButton::Middle, cx, cy), (1, false, true) => Mouse::Drag(MouseButton::Middle, cx, cy), (2, true, _) => Mouse::Up(MouseButton::Right, cx, cy), (2, false, false) => Mouse::Down(MouseButton::Right, cx, cy), (2, false, true) => Mouse::Drag(MouseButton::Right, cx, cy), _ => return None, } }; Some(Sequence::Mouse(mouse, modifiers)) } fn parse_csi_rxvt_mouse(parameters: &[u64]) -> Option { // ESC [ Cb ; Cx ; Cy ; M if parameters.len() < 3 { return None; } let cb = parameters[0]; let cx = parameters[1] as u16; let cy = parameters[2] as u16; let mut modifiers = KeyModifiers::empty(); if cb & 0b0000_0100 == 0b0000_0100 { modifiers |= KeyModifiers::SHIFT; } if cb & 0b0000_1000 == 0b0000_1000 { modifiers |= KeyModifiers::ALT; } if cb & 0b0001_0000 == 0b0001_0000 { modifiers |= KeyModifiers::CONTROL; } let mouse = if cb & 0b0110_0000 == 0b0110_0000 { if cb & 0b0000_0001 == 0b0000_0001 { Mouse::ScrollDown(cx, cy) } else { Mouse::ScrollUp(cx, cy) } } else { let drag = cb & 0b0100_0000 == 0b0100_0000; match (cb & 0b0000_0011, drag) { (0b0000_0000, false) => Mouse::Down(MouseButton::Left, cx, cy), (0b0000_0010, false) => Mouse::Down(MouseButton::Right, cx, cy), (0b0000_0001, false) => Mouse::Down(MouseButton::Middle, cx, cy), (0b0000_0000, true) => Mouse::Drag(MouseButton::Left, cx, cy), (0b0000_0010, true) => Mouse::Drag(MouseButton::Right, cx, cy), (0b0000_0001, true) => Mouse::Drag(MouseButton::Middle, cx, cy), (0b0000_0011, false) => Mouse::Up(MouseButton::Any, cx, cy), _ => return None, } }; Some(Sequence::Mouse(mouse, modifiers)) } anes-0.1.6/src/parser/types.rs010064400007650000024000000035721357023236000144700ustar0000000000000000use bitflags::bitflags; /// A parsed ANSI escape sequence. /// /// Check the [`Parser`](struct.Parser.html) structure documentation for examples /// how to retrieve these values. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum Sequence { /// A keyboard event sequence. Key(KeyCode, KeyModifiers), /// A mouse event sequence. Mouse(Mouse, KeyModifiers), /// A cursor position (`x`, `y`). /// /// Top/left cell is represented as `Sequence::CursorPosition(1, 1)`. CursorPosition(u16, u16), } bitflags! { /// A key modifiers. pub struct KeyModifiers: u8 { const SHIFT = 0b0000_0001; const CONTROL = 0b0000_0010; const ALT = 0b0000_0100; const META = 0b0000_1000; } } /// A key code. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum KeyCode { Backspace, Enter, Left, Right, Up, Down, Home, End, PageUp, PageDown, Tab, BackTab, Delete, Insert, F(u8), Char(char), Null, Esc, } /// A mouse event. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum Mouse { /// A mouse button press. Down(MouseButton, u16, u16), /// A mouse button release. Up(MouseButton, u16, u16), /// A mouse movement with pressed button. Drag(MouseButton, u16, u16), /// A mouse wheel scrolled up. ScrollUp(u16, u16), /// A mouse wheel scrolled down. ScrollDown(u16, u16), } /// A mouse button. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum MouseButton { Left, Right, Middle, /// This variant is provided only if [`Parser`](struct.Parser.html) doesn't know which /// mouse button was pressed/released. /// /// An example is [rxvt](https://en.wikipedia.org/wiki/Rxvt) - it provides which mouse /// button was pressed, but doesn't provide which mouse button was released. Any, } anes-0.1.6/src/sequences.rs010064400007650000024000000001671357023236000140200ustar0000000000000000pub(crate) mod attribute; pub(crate) mod buffer; pub(crate) mod color; pub(crate) mod cursor; pub(crate) mod terminal; anes-0.1.6/src/sequences/attribute.rs010064400007650000024000000067471357023236000160350ustar0000000000000000use std::fmt; sequence!( /// Resets all attributes. /// /// This sequence resets all attributes previously set by the: /// /// * [`SetAttribute`](struct.SetAttribute.html) /// * [`SetForegroundColor`](struct.SetBackgroundColor.html) /// * [`SetBackgroundColor`](struct.SetForegroundColor.html) /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::ResetAttributes; /// /// let mut stdout = stdout(); /// write!(stdout, "{}", ResetAttributes); /// ``` struct ResetAttributes => sgr!("0") ); /// A display attribute. /// /// This is **NOT** a full ANSI sequence. `Attribute` must be used along with /// the [`SetAttribute`](struct.SetAttribute.html). /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::{Attribute, SetAttribute}; /// /// let mut stdout = stdout(); /// write!(stdout, "{}Bold text", SetAttribute(Attribute::Bold)); /// ``` #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] pub enum Attribute { /// Bold (increased) intensity. Bold = 1, /// Faint (decreased) intensity. Faint = 2, /// Normal intensity (turns off `Bold` and/or `Faint`). Normal = 22, /// Italic. Italic = 3, /// Turns off `Italic`. ItalicOff = 23, /// Underlined text. Underline = 4, /// Turns off `Underline`. UnderlineOff = 24, /// Blinking text. Blink = 5, /// Turns off blinking text (`Blink`). BlinkOff = 25, /// Reverse foreground & background colors. Reverse = 7, /// Turns off `Reverse`. ReverseOff = 27, /// Concealed (hidden). Conceal = 8, /// Turns off `Conceal`. ConcealOff = 28, /// Crossed. Crossed = 9, /// Turns off `Crossed`. CrossedOff = 29, } impl fmt::Display for Attribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", *self as i32) } } sequence!( /// Sets the display attribute. /// /// See the [`Attribute`](enum.Attribute.html) enum for a list of attributes you can (un)set. /// /// The [`ResetAttributes`](struct.ResetAttributes.html) sequence can be used to turn off all /// attributes. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::{Attribute, SetAttribute}; /// /// let mut stdout = stdout(); /// write!(stdout, "{}Blinking text", SetAttribute(Attribute::Blink)); /// ``` struct SetAttribute(Attribute) => |this, f| write!(f, sgr!("{}"), this.0) ); #[cfg(test)] test_sequences!( set_attribute( SetAttribute(Attribute::Bold) => "\x1B[1m", SetAttribute(Attribute::Faint) => "\x1B[2m", SetAttribute(Attribute::Normal) => "\x1B[22m", SetAttribute(Attribute::Italic) => "\x1B[3m", SetAttribute(Attribute::ItalicOff) => "\x1B[23m", SetAttribute(Attribute::Underline) => "\x1B[4m", SetAttribute(Attribute::UnderlineOff) => "\x1B[24m", SetAttribute(Attribute::Blink) => "\x1B[5m", SetAttribute(Attribute::BlinkOff) => "\x1B[25m", SetAttribute(Attribute::Reverse) => "\x1B[7m", SetAttribute(Attribute::ReverseOff) => "\x1B[27m", SetAttribute(Attribute::Conceal) => "\x1B[8m", SetAttribute(Attribute::ConcealOff) => "\x1B[28m", SetAttribute(Attribute::Crossed) => "\x1B[9m", SetAttribute(Attribute::CrossedOff) => "\x1B[29m", ), reset_attributes( ResetAttributes => "\x1B[0m", ) ); anes-0.1.6/src/sequences/buffer.rs010064400007650000024000000074421357023236000152740ustar0000000000000000sequence!( /// Switches to the alternate buffer. /// /// Use the [`SwitchBufferToNormal`](struct.SwitchBufferToNormal.html) sequence to switch /// back to the normal buffer. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::{SwitchBufferToAlternate, SwitchBufferToNormal}; /// /// let mut stdout = stdout(); /// write!(stdout, "{}", SwitchBufferToAlternate); /// // Your app on alternate screen /// write!(stdout, "{}", SwitchBufferToNormal); /// ``` struct SwitchBufferToAlternate => csi!("?1049h") ); sequence!( /// Switches to the normal buffer. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::{SwitchBufferToAlternate, SwitchBufferToNormal}; /// /// let mut stdout = stdout(); /// write!(stdout, "{}", SwitchBufferToAlternate); /// // Your app on alternate screen /// write!(stdout, "{}", SwitchBufferToNormal); /// ``` struct SwitchBufferToNormal => csi!("?1049l") ); sequence!( /// Scrolls up by the given number of rows. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::ScrollBufferUp; /// /// let mut stdout = stdout(); /// // Scroll up by 5 lines /// write!(stdout, "{}", ScrollBufferUp(5)); /// ``` struct ScrollBufferUp(u16) => |this, f| write!(f, csi!("{}S"), this.0) ); sequence!( /// Scrolls down by the given number of rows. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::ScrollBufferDown; /// /// let mut stdout = stdout(); /// // Scroll down by 10 lines /// write!(stdout, "{}", ScrollBufferDown(10)); /// ``` struct ScrollBufferDown(u16) => |this, f| write!(f, csi!("{}T"), this.0) ); sequence!( /// Clears part of the line. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::ClearLine; /// /// let mut stdout = stdout(); /// // Clear the whole line /// write!(stdout, "{}", ClearLine::All); /// ``` enum ClearLine { /// Clears from the cursor position to end of the line. Right => csi!("K"), /// Clears from the cursor position to beginning of the line. Left => csi!("1K"), /// Clears the whole line. All => csi!("2K"), } ); sequence!( /// Clears part of the buffer. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::ClearBuffer; /// /// let mut stdout = stdout(); /// // Clear the entire buffer /// write!(stdout, "{}", ClearBuffer::All); /// ``` enum ClearBuffer { /// Clears from the cursor position to end of the screen. Below => csi!("J"), /// Clears from the cursor position to beginning of the screen. Above => csi!("1J"), /// Clears the entire buffer. All => csi!("2J"), /// Clears the entire buffer and all saved lines in the scrollback buffer. SavedLines => csi!("3J"), } ); #[cfg(test)] test_sequences!( switch_buffer_to_alternate( SwitchBufferToAlternate => "\x1B[?1049h", ), switch_buffer_to_main( SwitchBufferToNormal => "\x1B[?1049l", ), scroll_buffer_up( ScrollBufferUp(10) => "\x1B[10S", ), scroll_buffer_down( ScrollBufferDown(10) => "\x1B[10T", ), clear_line( ClearLine::Right => "\x1B[K", ClearLine::Left => "\x1B[1K", ClearLine::All => "\x1B[2K", ), clear_buffer( ClearBuffer::Below => "\x1B[J", ClearBuffer::Above => "\x1B[1J", ClearBuffer::All => "\x1B[2J", ClearBuffer::SavedLines => "\x1B[3J", ), ); anes-0.1.6/src/sequences/color.rs010064400007650000024000000150361357023236000151370ustar0000000000000000use std::fmt; /// A color. /// /// This is **NOT** a full ANSI sequence. `Color` must be used along with /// the: /// /// * [`SetBackgroundColor`](struct.SetBackgroundColor.html) /// * [`SetForegroundColor`](struct.SetForegroundColor.html) /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::{Color, SetForegroundColor}; /// /// let mut stdout = stdout(); /// // Set the foreground color to red /// write!(stdout, "{}", SetForegroundColor(Color::Red)); /// ``` #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] pub enum Color { /// Default color. Default, /// Black color. Black, /// Dark red color. DarkRed, /// Dark green color. DarkGreen, /// Dark yellow color. DarkYellow, /// Dark blue color. DarkBlue, /// Dark magenta color. DarkMagenta, /// Dark cyan color. DarkCyan, /// Dark gray color. /// /// Also knows as light (bright) black. DarkGray, /// Light (bright) gray color. /// /// Also known as dark white. Gray, /// Light (bright) red color. Red, /// Light (bright) green color. Green, /// Light (bright) yellow color. Yellow, /// Light (bright) blue color. Blue, /// Light (bright) magenta color. Magenta, /// Light (bright) cyan color. Cyan, /// White color. White, /// A color from the predefined set of ANSI colors. /// /// ```text /// 0 - 7: standard colors (as in ESC [ 30–37 m) /// 8- 15: high intensity colors (as in ESC [ 90–97 m) /// 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) /// 232-255: grayscale from black to white in 24 steps /// ``` /// /// See [8-bit](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) for more information. Ansi(u8), /// An RGB color. /// /// See [24-bit](https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit) for more information. Rgb(u8, u8, u8), } impl fmt::Display for Color { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { // Color::Default is handled in the SetBackgroundColor & SetForegroundColor Color::Default => Ok(()), Color::Black => write!(f, "5;0"), Color::DarkRed => write!(f, "5;1"), Color::DarkGreen => write!(f, "5;2"), Color::DarkYellow => write!(f, "5;3"), Color::DarkBlue => write!(f, "5;4"), Color::DarkMagenta => write!(f, "5;5"), Color::DarkCyan => write!(f, "5;6"), Color::Gray => write!(f, "5;7"), Color::DarkGray => write!(f, "5;8"), Color::Red => write!(f, "5;9"), Color::Green => write!(f, "5;10"), Color::Yellow => write!(f, "5;11"), Color::Blue => write!(f, "5;12"), Color::Magenta => write!(f, "5;13"), Color::Cyan => write!(f, "5;14"), Color::White => write!(f, "5;15"), Color::Ansi(value) => write!(f, "5;{}", value), Color::Rgb(r, g, b) => write!(f, "2;{};{};{}", r, g, b), } } } sequence! { /// Sets the foreground color. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::{Color, SetForegroundColor}; /// /// let mut stdout = stdout(); /// // Set the foreground color to blue /// write!(stdout, "{}", SetForegroundColor(Color::Blue)); /// ``` struct SetForegroundColor(Color) => |this, f| match this.0 { Color::Default => write!(f, sgr!("39")), _ => write!(f, sgr!("38;{}"), this.0), } } sequence! { /// Sets the background color. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::{Color, SetBackgroundColor}; /// /// let mut stdout = stdout(); /// // Set the background color to yellow /// write!(stdout, "{}", SetBackgroundColor(Color::Yellow)); /// ``` struct SetBackgroundColor(Color) => |this, f| match this.0 { Color::Default => write!(f, sgr!("49")), _ => write!(f, sgr!("48;{}"), this.0), } } #[cfg(test)] test_sequences!( set_foreground_color( SetForegroundColor(Color::Default) => "\x1B[39m", SetForegroundColor(Color::Black) => "\x1B[38;5;0m", SetForegroundColor(Color::DarkRed) => "\x1B[38;5;1m", SetForegroundColor(Color::DarkGreen) => "\x1B[38;5;2m", SetForegroundColor(Color::DarkYellow) => "\x1B[38;5;3m", SetForegroundColor(Color::DarkBlue) => "\x1B[38;5;4m", SetForegroundColor(Color::DarkMagenta) => "\x1B[38;5;5m", SetForegroundColor(Color::DarkCyan) => "\x1B[38;5;6m", SetForegroundColor(Color::DarkGray) => "\x1B[38;5;8m", SetForegroundColor(Color::Gray) => "\x1B[38;5;7m", SetForegroundColor(Color::Red) => "\x1B[38;5;9m", SetForegroundColor(Color::Green) => "\x1B[38;5;10m", SetForegroundColor(Color::Yellow) => "\x1B[38;5;11m", SetForegroundColor(Color::Blue) => "\x1B[38;5;12m", SetForegroundColor(Color::Magenta) => "\x1B[38;5;13m", SetForegroundColor(Color::Cyan) => "\x1B[38;5;14m", SetForegroundColor(Color::White) => "\x1B[38;5;15m", SetForegroundColor(Color::Ansi(200)) => "\x1B[38;5;200m", SetForegroundColor(Color::Rgb(1, 2, 3)) => "\x1B[38;2;1;2;3m", ), set_background_color( SetBackgroundColor(Color::Default) => "\x1B[49m", SetBackgroundColor(Color::Black) => "\x1B[48;5;0m", SetBackgroundColor(Color::DarkRed) => "\x1B[48;5;1m", SetBackgroundColor(Color::DarkGreen) => "\x1B[48;5;2m", SetBackgroundColor(Color::DarkYellow) => "\x1B[48;5;3m", SetBackgroundColor(Color::DarkBlue) => "\x1B[48;5;4m", SetBackgroundColor(Color::DarkMagenta) => "\x1B[48;5;5m", SetBackgroundColor(Color::DarkCyan) => "\x1B[48;5;6m", SetBackgroundColor(Color::DarkGray) => "\x1B[48;5;8m", SetBackgroundColor(Color::Gray) => "\x1B[48;5;7m", SetBackgroundColor(Color::Red) => "\x1B[48;5;9m", SetBackgroundColor(Color::Green) => "\x1B[48;5;10m", SetBackgroundColor(Color::Yellow) => "\x1B[48;5;11m", SetBackgroundColor(Color::Blue) => "\x1B[48;5;12m", SetBackgroundColor(Color::Magenta) => "\x1B[48;5;13m", SetBackgroundColor(Color::Cyan) => "\x1B[48;5;14m", SetBackgroundColor(Color::White) => "\x1B[48;5;15m", SetBackgroundColor(Color::Ansi(200)) => "\x1B[48;5;200m", SetBackgroundColor(Color::Rgb(1, 2, 3)) => "\x1B[48;2;1;2;3m", ) ); anes-0.1.6/src/sequences/cursor.rs010064400007650000024000000217061357023236000153370ustar0000000000000000//! A terminal cursor related ANSI escape sequences. sequence!( /// Saves the cursor position. /// /// Use the [`RestoreCursorPosition`](struct.RestoreCursorPosition.html) sequence to /// restore the cursor position. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::{SaveCursorPosition, RestoreCursorPosition}; /// /// let mut stdout = stdout(); /// // Save cursor position /// write!(stdout, "{}", SaveCursorPosition); /// /// // Your app /// /// // Restore cursor position /// write!(stdout, "{}", RestoreCursorPosition); /// ``` struct SaveCursorPosition => esc!("7") ); sequence!( /// Restores the cursor position. /// /// Use the [`SaveCursorPosition`](struct.SaveCursorPosition.html) sequence to /// save the cursor position. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::{SaveCursorPosition, RestoreCursorPosition}; /// /// let mut stdout = stdout(); /// // Save cursor position /// write!(stdout, "{}", SaveCursorPosition); /// /// // Your app /// /// // Restore cursor position /// write!(stdout, "{}", RestoreCursorPosition); /// ``` struct RestoreCursorPosition => esc!("8") ); sequence!( /// Hides the cursor. /// /// Use the [`ShowCursor`](struct.ShowCursor.html) sequence to show the cursor. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::HideCursor; /// /// let mut stdout = stdout(); /// // Hide cursor /// write!(stdout, "{}", HideCursor); /// ``` struct HideCursor => csi!("?25l") ); sequence!( /// Shows the cursor. /// /// Use the [`HideCursor`](struct.HideCursor.html) sequence to hide the cursor. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::ShowCursor; /// /// let mut stdout = stdout(); /// // Show cursor /// write!(stdout, "{}", ShowCursor); /// ``` struct ShowCursor => csi!("?25h") ); sequence!( /// Enables the cursor blinking. /// /// Use the [`DisableCursorBlinking`](struct.DisableCursorBlinking.html) sequence to disable /// cursor blinking. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::EnableCursorBlinking; /// /// let mut stdout = stdout(); /// // Enable cursor blinking /// write!(stdout, "{}", EnableCursorBlinking); /// ``` struct EnableCursorBlinking => csi!("?12h") ); sequence!( /// Disables the cursor blinking. /// /// Use the [`EnableCursorBlinking`](struct.EnableCursorBlinking.html) sequence to enable /// cursor blinking. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::DisableCursorBlinking; /// /// let mut stdout = stdout(); /// // Disable cursor blinking /// write!(stdout, "{}", DisableCursorBlinking); /// ``` struct DisableCursorBlinking => csi!("?12l") ); sequence!( /// Moves the cursor to the given location (column, row). /// /// # Notes /// /// Top/left cell is represented as `1, 1` (`column, row`). /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::MoveCursorTo; /// /// let mut stdout = stdout(); /// // Move cursor to top left cell /// write!(stdout, "{}", MoveCursorTo(1, 1)); /// ``` struct MoveCursorTo(u16, u16) => |this, f| write!(f, csi!("{};{}H"), this.1, this.0) ); sequence!( /// Moves the cursor up by the given number of rows. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::MoveCursorUp; /// /// let mut stdout = stdout(); /// // Move cursor up by 5 rows /// write!(stdout, "{}", MoveCursorUp(5)); /// ``` struct MoveCursorUp(u16) => |this, f| write!(f, csi!("{}A"), this.0) ); sequence!( /// Moves the cursor down by the given number of rows. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::MoveCursorDown; /// /// let mut stdout = stdout(); /// // Move cursor down by 5 rows /// write!(stdout, "{}", MoveCursorDown(5)); /// ``` struct MoveCursorDown(u16) => |this, f| write!(f, csi!("{}B"), this.0) ); sequence!( /// Moves the cursor right by the given number of columns. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::MoveCursorRight; /// /// let mut stdout = stdout(); /// // Move cursor right by 5 columns /// write!(stdout, "{}", MoveCursorRight(5)); /// ``` struct MoveCursorRight(u16) => |this, f| write!(f, csi!("{}C"), this.0) ); sequence!( /// Moves the cursor left by the given number of columns. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::MoveCursorLeft; /// /// let mut stdout = stdout(); /// // Move cursor left by 5 columns /// write!(stdout, "{}", MoveCursorLeft(5)); /// ``` struct MoveCursorLeft(u16) => |this, f| write!(f, csi!("{}D"), this.0) ); sequence!( /// Moves the cursor to beginning of line the given number of lines down. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::MoveCursorToNextLine; /// /// let mut stdout = stdout(); /// // Move cursor down by 2 rows and the move it to the first column /// write!(stdout, "{}", MoveCursorToNextLine(2)); /// ``` /// /// The previous example does the same thing as the following one: /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::{MoveCursorDown, MoveCursorToColumn}; /// /// let mut stdout = stdout(); /// write!(stdout, "{}{}", MoveCursorDown(2), MoveCursorToColumn(1)); /// ``` struct MoveCursorToNextLine(u16) => |this, f| write!(f, csi!("{}E"), this.0) ); sequence!( /// Moves the cursor to beginning of line the given number of lines up. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::MoveCursorToPreviousLine; /// /// let mut stdout = stdout(); /// // Move cursor up by 2 rows and the move it to the first column /// write!(stdout, "{}", MoveCursorToPreviousLine(2)); /// ``` /// /// The previous example does the same thing as the following one: /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::{MoveCursorUp, MoveCursorToColumn}; /// /// let mut stdout = stdout(); /// write!(stdout, "{}{}", MoveCursorUp(2), MoveCursorToColumn(1)); /// ``` struct MoveCursorToPreviousLine(u16) => |this, f| write!(f, csi!("{}F"), this.0) ); sequence!( /// Moves the cursor to the given column. /// /// # Notes /// /// Beginning of the line (left cell) is represented as `1`. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::MoveCursorToColumn; /// /// let mut stdout = stdout(); /// // Move cursor to the 10th column (same row) /// write!(stdout, "{}", MoveCursorToColumn(10)); /// ``` struct MoveCursorToColumn(u16) => |this, f| write!(f, csi!("{}G"), this.0) ); // TODO Enhance example with Parser to show how to retrieve it sequence!( /// Asks for the current cursor position. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::ReportCursorPosition; /// /// let mut stdout = stdout(); /// write!(stdout, "{}", ReportCursorPosition); /// ``` struct ReportCursorPosition => csi!("6n") ); #[cfg(test)] test_sequences!( save_cursor_position( SaveCursorPosition => "\x1B7", ), restore_cursor_position( RestoreCursorPosition => "\x1B8", ), hide_cursor( HideCursor => "\x1B[?25l", ), show_cursor( ShowCursor => "\x1B[?25h", ), disable_cursor_blinking( DisableCursorBlinking => "\x1B[?12l", ), enable_cursor_blinking( EnableCursorBlinking => "\x1B[?12h", ), move_cursor_up( MoveCursorUp(10) => "\x1B[10A", ), move_cursor_down( MoveCursorDown(10) => "\x1B[10B", ), move_cursor_right( MoveCursorRight(10) => "\x1B[10C", ), move_cursor_left( MoveCursorLeft(10) => "\x1B[10D", ), move_cursor_to( MoveCursorTo(5, 10) => "\x1B[10;5H", ), move_cursor_to_next_line( MoveCursorToNextLine(5) => "\x1B[5E", ), move_cursor_to_previous_line( MoveCursorToPreviousLine(5) => "\x1B[5F", ), move_cursor_to_column( MoveCursorToColumn(1) => "\x1B[1G", ), report_cursor_position( ReportCursorPosition => "\x1B[6n", ) ); anes-0.1.6/src/sequences/terminal.rs010064400007650000024000000025511357023467500156450ustar0000000000000000//! A terminal related ANSI escape sequences. sequence!( /// Resizes the text area to the given width and height in characters. /// /// # Examples /// /// ```no_run /// use std::io::{stdout, Write}; /// use anes::ResizeTextArea; /// /// let mut stdout = stdout(); /// // Resize the terminal to 80x25 /// write!(stdout, "{}", ResizeTextArea(80, 25)); /// ``` struct ResizeTextArea(u16, u16) => |this, f| write!(f, csi!("8;{};{}t"), this.1, this.0) ); sequence!( /// Tells the terminal to start reporting mouse events. /// /// Mouse events are not reported by default. struct EnableMouseEvents => concat!( csi!("?1000h"), csi!("?1002h"), csi!("?1015h"), csi!("?1006h") ) ); sequence!( /// Tells the terminal to stop reporting mouse events. struct DisableMouseEvents => concat!( csi!("?1006l"), csi!("?1015l"), csi!("?1002l"), csi!("?1000l") ) ); #[cfg(test)] test_sequences!( resize_text_area( ResizeTextArea(80, 25) => "\x1B[8;25;80t", ResizeTextArea(1, 1) => "\x1B[8;1;1t", ), enable_mouse_events( EnableMouseEvents => "\x1B[?1000h\x1B[?1002h\x1B[?1015h\x1B[?1006h", ), disable_mouse_events( DisableMouseEvents => "\x1B[?1006l\x1B[?1015l\x1B[?1002l\x1B[?1000l", ) ); anes-0.1.6/tests/parser/cursor.rs010064400007650000024000000002331357023236000152030ustar0000000000000000use anes::parser::Sequence; use crate::test_sequences; #[test] fn position() { test_sequences!(b"\x1B[20;10R", Sequence::CursorPosition(10, 20),); } anes-0.1.6/tests/parser/key.rs010064400007650000024000000121701357023236000144610ustar0000000000000000use anes::parser::{KeyCode, KeyModifiers, Sequence}; use crate::test_sequences; #[test] fn esc_o_f_keys() { test_sequences!( b"\x1BOP", Sequence::Key(KeyCode::F(1), KeyModifiers::empty()), b"\x1BOQ", Sequence::Key(KeyCode::F(2), KeyModifiers::empty()), b"\x1BOR", Sequence::Key(KeyCode::F(3), KeyModifiers::empty()), b"\x1BOS", Sequence::Key(KeyCode::F(4), KeyModifiers::empty()), ); } #[test] fn csi_key_codes() { test_sequences!( b"\x1B[A", Sequence::Key(KeyCode::Up, KeyModifiers::empty()), b"\x1B[B", Sequence::Key(KeyCode::Down, KeyModifiers::empty()), b"\x1B[C", Sequence::Key(KeyCode::Right, KeyModifiers::empty()), b"\x1B[D", Sequence::Key(KeyCode::Left, KeyModifiers::empty()), b"\x1B[H", Sequence::Key(KeyCode::Home, KeyModifiers::empty()), b"\x1B[F", Sequence::Key(KeyCode::End, KeyModifiers::empty()), b"\x1B[Z", Sequence::Key(KeyCode::BackTab, KeyModifiers::empty()), ); } #[test] fn csi_arrow_key_modifiers() { test_sequences!( b"\x1B[50A", Sequence::Key(KeyCode::Up, KeyModifiers::SHIFT), b"\x1B[53A", Sequence::Key(KeyCode::Up, KeyModifiers::CONTROL), ); } #[test] fn csi_tilde_key_modifiers() { test_sequences!( b"\x1B[1~", Sequence::Key(KeyCode::Home, KeyModifiers::empty()), b"\x1B[1;0~", Sequence::Key(KeyCode::Home, KeyModifiers::empty()), b"\x1B[1;1~", Sequence::Key(KeyCode::Home, KeyModifiers::empty()), b"\x1B[1;2~", Sequence::Key(KeyCode::Home, KeyModifiers::SHIFT), b"\x1B[1;3~", Sequence::Key(KeyCode::Home, KeyModifiers::ALT), b"\x1B[1;4~", Sequence::Key(KeyCode::Home, KeyModifiers::SHIFT | KeyModifiers::ALT), b"\x1B[1;5~", Sequence::Key(KeyCode::Home, KeyModifiers::CONTROL), b"\x1B[1;6~", Sequence::Key(KeyCode::Home, KeyModifiers::SHIFT | KeyModifiers::CONTROL), b"\x1B[1;7~", Sequence::Key(KeyCode::Home, KeyModifiers::ALT | KeyModifiers::CONTROL), b"\x1B[1;8~", Sequence::Key( KeyCode::Home, KeyModifiers::SHIFT | KeyModifiers::ALT | KeyModifiers::CONTROL ), b"\x1B[1;9~", Sequence::Key(KeyCode::Home, KeyModifiers::META), b"\x1B[1;10~", Sequence::Key(KeyCode::Home, KeyModifiers::META | KeyModifiers::SHIFT), b"\x1B[1;11~", Sequence::Key(KeyCode::Home, KeyModifiers::META | KeyModifiers::ALT), b"\x1B[1;12~", Sequence::Key( KeyCode::Home, KeyModifiers::META | KeyModifiers::SHIFT | KeyModifiers::ALT ), b"\x1B[1;13~", Sequence::Key(KeyCode::Home, KeyModifiers::META | KeyModifiers::CONTROL), b"\x1B[1;14~", Sequence::Key( KeyCode::Home, KeyModifiers::META | KeyModifiers::SHIFT | KeyModifiers::CONTROL ), b"\x1B[1;15~", Sequence::Key( KeyCode::Home, KeyModifiers::META | KeyModifiers::ALT | KeyModifiers::CONTROL ), b"\x1B[1;16~", Sequence::Key( KeyCode::Home, KeyModifiers::META | KeyModifiers::SHIFT | KeyModifiers::ALT | KeyModifiers::CONTROL ), b"\x1B[1;17~", Sequence::Key(KeyCode::Home, KeyModifiers::empty()), ); } #[test] fn csi_tilde_f_keys() { test_sequences!( b"\x1B[11~", Sequence::Key(KeyCode::F(1), KeyModifiers::empty()), b"\x1B[12~", Sequence::Key(KeyCode::F(2), KeyModifiers::empty()), b"\x1B[13~", Sequence::Key(KeyCode::F(3), KeyModifiers::empty()), b"\x1B[14~", Sequence::Key(KeyCode::F(4), KeyModifiers::empty()), b"\x1B[15~", Sequence::Key(KeyCode::F(5), KeyModifiers::empty()), b"\x1B[17~", Sequence::Key(KeyCode::F(6), KeyModifiers::empty()), b"\x1B[18~", Sequence::Key(KeyCode::F(7), KeyModifiers::empty()), b"\x1B[19~", Sequence::Key(KeyCode::F(8), KeyModifiers::empty()), b"\x1B[20~", Sequence::Key(KeyCode::F(9), KeyModifiers::empty()), b"\x1B[21~", Sequence::Key(KeyCode::F(10), KeyModifiers::empty()), b"\x1B[23~", Sequence::Key(KeyCode::F(11), KeyModifiers::empty()), b"\x1B[24~", Sequence::Key(KeyCode::F(12), KeyModifiers::empty()), ); } #[test] fn csi_tilde_key_codes() { test_sequences!( b"\x1B[1~", Sequence::Key(KeyCode::Home, KeyModifiers::empty()), b"\x1B[2~", Sequence::Key(KeyCode::Insert, KeyModifiers::empty()), b"\x1B[3~", Sequence::Key(KeyCode::Delete, KeyModifiers::empty()), b"\x1B[4~", Sequence::Key(KeyCode::End, KeyModifiers::empty()), b"\x1B[5~", Sequence::Key(KeyCode::PageUp, KeyModifiers::empty()), b"\x1B[6~", Sequence::Key(KeyCode::PageDown, KeyModifiers::empty()), b"\x1B[7~", Sequence::Key(KeyCode::Home, KeyModifiers::empty()), b"\x1B[8~", Sequence::Key(KeyCode::End, KeyModifiers::empty()), ); } anes-0.1.6/tests/parser/mod.rs010064400007650000024000000007161357023236000144530ustar0000000000000000#[macro_export] macro_rules! test_sequence { ($bytes:expr, $seq:expr) => { let mut parser = ::anes::parser::Parser::default(); parser.advance($bytes, false); assert_eq!(parser.next(), Some($seq)); }; } #[macro_export] macro_rules! test_sequences { ( $( $bytes:expr, $seq:expr, )* ) => { $( test_sequence!($bytes, $seq); )* }; } mod cursor; mod key; mod mouse; anes-0.1.6/tests/parser/mouse/mod.rs010064400007650000024000000000251357023236000155740ustar0000000000000000mod rxvt; mod xterm; anes-0.1.6/tests/parser/mouse/rxvt.rs010064400007650000024000000077751357023236000160430ustar0000000000000000use anes::parser::{KeyModifiers, Mouse, MouseButton, Sequence}; use crate::test_sequences; #[test] fn button_down() { test_sequences!( b"\x1B[0;30;40;M", Sequence::Mouse( Mouse::Down(MouseButton::Left, 30, 40), KeyModifiers::empty() ), b"\x1B[1;30;40;M", Sequence::Mouse( Mouse::Down(MouseButton::Middle, 30, 40), KeyModifiers::empty() ), b"\x1B[2;30;40;M", Sequence::Mouse( Mouse::Down(MouseButton::Right, 30, 40), KeyModifiers::empty() ), ); } #[test] fn button_down_with_modifiers() { test_sequences!( b"\x1B[4;30;40;M", Sequence::Mouse(Mouse::Down(MouseButton::Left, 30, 40), KeyModifiers::SHIFT), b"\x1B[5;30;40;M", Sequence::Mouse( Mouse::Down(MouseButton::Middle, 30, 40), KeyModifiers::SHIFT ), b"\x1B[6;30;40;M", Sequence::Mouse(Mouse::Down(MouseButton::Right, 30, 40), KeyModifiers::SHIFT), ); } #[test] fn button_up() { test_sequences!( b"\x1B[3;30;40;M", Sequence::Mouse(Mouse::Up(MouseButton::Any, 30, 40), KeyModifiers::empty()), ); } #[test] fn button_up_with_modifiers() { test_sequences!( b"\x1B[7;30;40;M", Sequence::Mouse(Mouse::Up(MouseButton::Any, 30, 40), KeyModifiers::SHIFT), ); } #[test] fn scroll() { test_sequences!( b"\x1B[96;30;40;M", Sequence::Mouse(Mouse::ScrollUp(30, 40), KeyModifiers::empty()), b"\x1B[97;30;40;M", Sequence::Mouse(Mouse::ScrollDown(30, 40), KeyModifiers::empty()), ); } #[test] fn scroll_with_modifiers() { test_sequences!( b"\x1B[100;30;40;M", Sequence::Mouse(Mouse::ScrollUp(30, 40), KeyModifiers::SHIFT), b"\x1B[101;30;40;M", Sequence::Mouse(Mouse::ScrollDown(30, 40), KeyModifiers::SHIFT), ); } #[test] fn drag() { test_sequences!( b"\x1B[64;30;40;M", Sequence::Mouse( Mouse::Drag(MouseButton::Left, 30, 40), KeyModifiers::empty() ), b"\x1B[65;30;40;M", Sequence::Mouse( Mouse::Drag(MouseButton::Middle, 30, 40), KeyModifiers::empty() ), b"\x1B[66;30;40;M", Sequence::Mouse( Mouse::Drag(MouseButton::Right, 30, 40), KeyModifiers::empty() ), ); } #[test] fn drag_with_modifiers() { test_sequences!( b"\x1B[64;30;40;M", Sequence::Mouse( Mouse::Drag(MouseButton::Left, 30, 40), KeyModifiers::empty() ), b"\x1B[65;30;40;M", Sequence::Mouse( Mouse::Drag(MouseButton::Middle, 30, 40), KeyModifiers::empty() ), b"\x1B[66;30;40;M", Sequence::Mouse( Mouse::Drag(MouseButton::Right, 30, 40), KeyModifiers::empty() ), ); } #[test] fn key_modifier_combinations() { test_sequences!( b"\x1B[4;20;10M", Sequence::Mouse(Mouse::Down(MouseButton::Left, 20, 10), KeyModifiers::SHIFT), b"\x1B[8;20;10M", Sequence::Mouse(Mouse::Down(MouseButton::Left, 20, 10), KeyModifiers::ALT), b"\x1B[16;20;10M", Sequence::Mouse( Mouse::Down(MouseButton::Left, 20, 10), KeyModifiers::CONTROL ), b"\x1B[12;20;10;M", Sequence::Mouse( Mouse::Down(MouseButton::Left, 20, 10), KeyModifiers::SHIFT | KeyModifiers::ALT ), b"\x1B[20;20;10;M", Sequence::Mouse( Mouse::Down(MouseButton::Left, 20, 10), KeyModifiers::SHIFT | KeyModifiers::CONTROL ), b"\x1B[24;20;10;M", Sequence::Mouse( Mouse::Down(MouseButton::Left, 20, 10), KeyModifiers::ALT | KeyModifiers::CONTROL ), b"\x1B[28;20;10;M", Sequence::Mouse( Mouse::Down(MouseButton::Left, 20, 10), KeyModifiers::SHIFT | KeyModifiers::ALT | KeyModifiers::CONTROL ), ); } anes-0.1.6/tests/parser/mouse/xterm.rs010064400007650000024000000106341357023236000161630ustar0000000000000000use anes::parser::{KeyModifiers, Mouse, MouseButton, Sequence}; use crate::test_sequences; #[test] fn button_down() { test_sequences!( b"\x1B[<0;20;10;M", Sequence::Mouse( Mouse::Down(MouseButton::Left, 20, 10), KeyModifiers::empty() ), b"\x1B[<1;20;10;M", Sequence::Mouse( Mouse::Down(MouseButton::Middle, 20, 10), KeyModifiers::empty() ), b"\x1B[<2;20;10;M", Sequence::Mouse( Mouse::Down(MouseButton::Right, 20, 10), KeyModifiers::empty() ), ); } #[test] fn button_down_with_key_modifiers() { test_sequences!( b"\x1B[<4;20;10;M", Sequence::Mouse(Mouse::Down(MouseButton::Left, 20, 10), KeyModifiers::SHIFT), b"\x1B[<5;20;10;M", Sequence::Mouse( Mouse::Down(MouseButton::Middle, 20, 10), KeyModifiers::SHIFT ), b"\x1B[<6;20;10;M", Sequence::Mouse(Mouse::Down(MouseButton::Right, 20, 10), KeyModifiers::SHIFT), ); } #[test] fn button_up() { test_sequences!( b"\x1B[<0;20;10;m", Sequence::Mouse(Mouse::Up(MouseButton::Left, 20, 10), KeyModifiers::empty()), b"\x1B[<1;20;10;m", Sequence::Mouse( Mouse::Up(MouseButton::Middle, 20, 10), KeyModifiers::empty() ), b"\x1B[<2;20;10;m", Sequence::Mouse(Mouse::Up(MouseButton::Right, 20, 10), KeyModifiers::empty()), ); } #[test] fn button_up_with_key_modifiers() { test_sequences!( b"\x1B[<4;20;10;m", Sequence::Mouse(Mouse::Up(MouseButton::Left, 20, 10), KeyModifiers::SHIFT), b"\x1B[<5;20;10;m", Sequence::Mouse(Mouse::Up(MouseButton::Middle, 20, 10), KeyModifiers::SHIFT), b"\x1B[<6;20;10;m", Sequence::Mouse(Mouse::Up(MouseButton::Right, 20, 10), KeyModifiers::SHIFT), ); } #[test] fn scroll() { test_sequences!( b"\x1B[<64;20;10;m", Sequence::Mouse(Mouse::ScrollUp(20, 10), KeyModifiers::empty()), b"\x1B[<65;20;10;m", Sequence::Mouse(Mouse::ScrollDown(20, 10), KeyModifiers::empty()), ); } #[test] fn scroll_with_key_modifiers() { test_sequences!( b"\x1B[<68;20;10;m", Sequence::Mouse(Mouse::ScrollUp(20, 10), KeyModifiers::SHIFT), b"\x1B[<69;20;10;m", Sequence::Mouse(Mouse::ScrollDown(20, 10), KeyModifiers::SHIFT), ); } #[test] fn drag() { test_sequences!( b"\x1B[<32;20;10;M", Sequence::Mouse( Mouse::Drag(MouseButton::Left, 20, 10), KeyModifiers::empty() ), b"\x1B[<33;20;10;M", Sequence::Mouse( Mouse::Drag(MouseButton::Middle, 20, 10), KeyModifiers::empty() ), b"\x1B[<34;20;10;M", Sequence::Mouse( Mouse::Drag(MouseButton::Right, 20, 10), KeyModifiers::empty() ), ); } #[test] fn drag_with_key_modifiers() { test_sequences!( b"\x1B[<36;20;10;M", Sequence::Mouse(Mouse::Drag(MouseButton::Left, 20, 10), KeyModifiers::SHIFT), b"\x1B[<37;20;10;M", Sequence::Mouse( Mouse::Drag(MouseButton::Middle, 20, 10), KeyModifiers::SHIFT, ), b"\x1B[<38;20;10;M", Sequence::Mouse(Mouse::Drag(MouseButton::Right, 20, 10), KeyModifiers::SHIFT), ); } #[test] fn key_modifier_combinations() { test_sequences!( b"\x1B[<4;20;10;m", Sequence::Mouse(Mouse::Up(MouseButton::Left, 20, 10), KeyModifiers::SHIFT), b"\x1B[<8;20;10;m", Sequence::Mouse(Mouse::Up(MouseButton::Left, 20, 10), KeyModifiers::ALT), b"\x1B[<16;20;10;m", Sequence::Mouse(Mouse::Up(MouseButton::Left, 20, 10), KeyModifiers::CONTROL), b"\x1B[<12;20;10;m", Sequence::Mouse( Mouse::Up(MouseButton::Left, 20, 10), KeyModifiers::SHIFT | KeyModifiers::ALT ), b"\x1B[<20;20;10;m", Sequence::Mouse( Mouse::Up(MouseButton::Left, 20, 10), KeyModifiers::SHIFT | KeyModifiers::CONTROL ), b"\x1B[<24;20;10;m", Sequence::Mouse( Mouse::Up(MouseButton::Left, 20, 10), KeyModifiers::ALT | KeyModifiers::CONTROL ), b"\x1B[<28;20;10;m", Sequence::Mouse( Mouse::Up(MouseButton::Left, 20, 10), KeyModifiers::SHIFT | KeyModifiers::ALT | KeyModifiers::CONTROL ), ); } anes-0.1.6/tests/tests.rs010064400007650000024000000000471357023236000135370ustar0000000000000000#[cfg(feature = "parser")] mod parser; anes-0.1.6/.cargo_vcs_info.json0000644000000001120000000000000120170ustar00{ "git": { "sha1": "378ac7461ecac29fde9e10df7b08359d1315a9ad" } } anes-0.1.6/Cargo.lock0000644000000546240000000000000100130ustar00# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "anes" version = "0.1.6" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "atty" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "autocfg" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bstr" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "byteorder" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cast" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "clap" version = "2.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "criterion" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "criterion-plot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_xoshiro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", "tinytemplate 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "criterion-plot" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-deque" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-epoch" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-queue" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-utils" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "csv" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "csv-core" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "either" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "getrandom" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "hermit-abi" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "itertools" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "itoa" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memchr" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "memoffset" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-traits" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num_cpus" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "proc-macro2" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_os" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_xoshiro" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rayon" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "rayon-core 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rayon-core" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-automata" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ryu" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "same-file" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "scopeguard" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tinytemplate" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unicode-width" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "walkdir" version = "2.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasi" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi-util" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "938703e165481c8d612ea3479ac8342e5615185db37765162e762ec3523e2fc6" "checksum criterion-plot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eccdc6ce8bbe352ca89025bee672aa6d24f4eb8c53e3a8b5d1bc58011da072a2" "checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" "checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" "checksum crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dfd6515864a82d2f877b42813d4553292c6659498c9a2aa31bab5a15243c2700" "checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" "checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d" "checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" "checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120" "checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" "checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" "checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72" "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" "checksum rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a788ae3edb696cfcba1c19bfd388cc4b8c21f8a408432b199c072825084da58a" "checksum rand_xoshiro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e18c91676f670f6f0312764c759405f13afb98d5d73819840cf72a518487bff" "checksum rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43739f8831493b276363637423d3622d4bd6394ab6f0a9c4a552e208aeb7fddd" "checksum rayon-core 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8bf17de6f23b05473c437eb958b9c850bfc8af0961fe17b4cc92d5a627b4791" "checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" "checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "1217f97ab8e8904b57dd22eb61cde455fa7446a9c1cf43966066da047c1f3702" "checksum serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "a8c6faef9a2e64b0064f48570289b4bf8823b7581f1d6157c1b52152306651d0" "checksum serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)" = "1a3351dcbc1f067e2c92ab7c3c1f288ad1a4cffc470b5aaddb4c2e0a3ae80043" "checksum syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f89693ae015201f8de93fd96bde2d065f8bfc3f97ce006d5bc9f900b97c0c7c0" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum tinytemplate 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4574b75faccaacddb9b284faecdf0b544b80b6b294f3d062d325c5726a209c20" "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" "checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"