Skip to content

Commit cceb288

Browse files
committed
support arbitrary crate
1 parent 2126c37 commit cceb288

File tree

9 files changed

+121
-3
lines changed

9 files changed

+121
-3
lines changed

Cargo.lock

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ name = "minimal"
2525
name = "serde"
2626
required-features = ["serde"]
2727

28+
[[example]]
29+
name = "arbitrary"
30+
required-features = ["arbitrary"]
31+
2832
[[example]]
2933
name = "crossterm"
3034
required-features = ["crossterm"]
@@ -43,8 +47,10 @@ serde = ["dep:serde"]
4347
crossterm = ["dep:crossterm"]
4448
winit = ["dep:winit"]
4549
termwiz = ["dep:termwiz"]
50+
arbitrary = ["dep:arbitrary"]
4651

4752
[dependencies]
53+
arbitrary = { version = "1.4.1", features = ["derive"], optional = true }
4854
bitflags = "2.8.0"
4955
crossterm = { version = "0.28.1", optional = true }
5056
serde = { version = "1.0.217", optional = true }
@@ -60,5 +66,5 @@ members = [".", "bench"]
6066

6167
[package.metadata.docs.rs]
6268
targets = ["x86_64-unknown-linux-gnu", "aarch64-apple-darwin", "x86_64-pc-windows-msvc"]
63-
features = ["serde", "crossterm", "winit", "termwiz"]
69+
features = ["serde", "crossterm", "winit", "termwiz", "arbitrary"]
6470
rustdoc-args = ["--cfg", "docsrs"]

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ library can be buggy and have arbitrary breaking changes.**
1616
- [crossterm][] ([example](./examples/crossterm.rs))
1717
- [termwiz][] ([example](./examples/termwiz.rs))
1818
- [winit][] ([example](./examples/winit.rs))
19+
- Support structral fuzzing using [arbitrary][] optionally. ([example](./examples/arbitrary.rs))
1920

2021
[API Documentation][api-doc]
2122

@@ -91,5 +92,6 @@ This crate is licensed under [the MIT license](./LICENSE.txt).
9192
[crossterm]: https://crates.io/crates/crossterm
9293
[winit]: https://crates.io/crates/winit
9394
[termwiz]: https://crates.io/crates/termwiz
95+
[arbitrary]: https://crates.io/crates/arbitrary
9496
[api-doc]: https://docs.rs/keybinds/latest/keybinds/
9597
[toml]: https://crates.io/crates/toml

examples/arbitrary.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use arbitrary::{Arbitrary, Result, Unstructured};
2+
use keybinds::{Key, KeyInput, KeySeq, Keybind, Keybinds, Mods};
3+
4+
// Actions dispatched by key bindings.
5+
#[derive(Arbitrary, Debug)]
6+
enum Action {
7+
Hello,
8+
Goodbye,
9+
}
10+
11+
fn main() -> Result<()> {
12+
let raw_data = b"
13+
Hello, an example for arbitrary crate support!
14+
This is the random data input from fuzzer.
15+
";
16+
let mut unstructured = Unstructured::new(raw_data);
17+
18+
// Generate arbitrary instances of types in keybinds crate
19+
println!("{:?}", Key::arbitrary(&mut unstructured)?);
20+
println!("{:?}", Mods::arbitrary(&mut unstructured)?);
21+
println!("{:?}", KeyInput::arbitrary(&mut unstructured)?);
22+
println!("{:?}", KeySeq::arbitrary(&mut unstructured)?);
23+
println!("{:?}", Keybind::<Action>::arbitrary(&mut unstructured)?);
24+
println!("{:?}", Keybinds::<Action>::arbitrary(&mut unstructured)?);
25+
26+
Ok(())
27+
}

src/arbitrary.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//! Support for [`arbitrary`] crate.
2+
//!
3+
//! This module provides [`Arbitrary`] trait support for several types in keybinds crate.
4+
//!
5+
//! ```
6+
//! use arbitrary::{Arbitrary, Result, Unstructured};
7+
//! use keybinds::{Key, KeyInput, KeySeq, Keybind, Keybinds, Mods};
8+
//!
9+
//! // Actions dispatched by key bindings.
10+
//! #[derive(Arbitrary, Debug)]
11+
//! enum Action {
12+
//! Hello,
13+
//! Goodbye,
14+
//! }
15+
//!
16+
//! let raw_data = b"
17+
//! Hello, the document for arbitrary crate support!
18+
//! This is the random data input from fuzzer.
19+
//! ";
20+
//! let mut unstructured = Unstructured::new(raw_data);
21+
//!
22+
//! // Generate arbitrary instances of types in keybinds crate
23+
//! let _ = Key::arbitrary(&mut unstructured).unwrap();
24+
//! let _ = Mods::arbitrary(&mut unstructured).unwrap();
25+
//! let _ = KeyInput::arbitrary(&mut unstructured).unwrap();
26+
//! let _ = KeySeq::arbitrary(&mut unstructured).unwrap();
27+
//! let _ = Keybind::<Action>::arbitrary(&mut unstructured).unwrap();
28+
//! let _ = Keybinds::<Action>::arbitrary(&mut unstructured).unwrap();
29+
//! ```
30+
use crate::Mods;
31+
use arbitrary::{Arbitrary, Result, Unstructured};
32+
33+
// Note: We don't use bitflags crate's `arbitrary` feature because it is quite inefficient.
34+
// Almost all bit patterns are generated by `Unstructured` are incorrect and they cause
35+
// 'Incorrect format' error which means generating an arbitrary instance failed.
36+
impl Arbitrary<'_> for Mods {
37+
fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {
38+
let mut mods = Self::NONE;
39+
mods.set(Mods::CTRL, u.arbitrary()?);
40+
mods.set(Mods::CMD, u.arbitrary()?);
41+
mods.set(Mods::ALT, u.arbitrary()?);
42+
mods.set(Mods::WIN, u.arbitrary()?);
43+
mods.set(Mods::SHIFT, u.arbitrary()?);
44+
Ok(mods)
45+
}
46+
}

src/key.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ use std::fmt;
44
use std::slice;
55
use std::str::FromStr;
66

7+
#[cfg(feature = "arbitrary")]
8+
use arbitrary::Arbitrary;
9+
710
#[non_exhaustive]
811
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
12+
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
913
pub enum Key {
1014
Char(char),
1115
Up,
@@ -248,6 +252,7 @@ impl fmt::Display for Mods {
248252
}
249253

250254
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
255+
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
251256
pub struct KeyInput {
252257
key: Key,
253258
mods: Mods,
@@ -316,6 +321,7 @@ pub enum Match {
316321
}
317322

318323
#[derive(Clone, Eq, Debug)]
324+
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
319325
pub enum KeySeq {
320326
Multiple(Vec<KeyInput>),
321327
Single(KeyInput),

src/keybind.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ use crate::{Key, KeyInput, KeySeq, Match, Result};
22
use std::ops::Deref;
33
use std::time::{Duration, Instant};
44

5+
#[cfg(feature = "arbitrary")]
6+
use arbitrary::Arbitrary;
7+
58
#[derive(Clone, PartialEq, Eq, Debug)]
9+
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
610
pub struct Keybind<A> {
711
pub seq: KeySeq,
812
pub action: A,
@@ -25,6 +29,7 @@ pub enum Found<'a, A> {
2529
}
2630

2731
#[derive(Clone, PartialEq, Eq, Debug)]
32+
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
2833
pub struct Keybinds<A>(Vec<Keybind<A>>);
2934

3035
impl<A> Default for Keybinds<A> {

src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//! - [crossterm][]
1111
//! - [termwiz][]
1212
//! - [winit][]
13+
//! - Support structral fuzzing using [arbitrary][] optionally.
1314
//!
1415
//! # Minimal example
1516
//!
@@ -66,6 +67,7 @@
6667
//! [crossterm]: https://crates.io/crates/crossterm
6768
//! [winit]: https://crates.io/crates/winit
6869
//! [termwiz]: https://crates.io/crates/termwiz
70+
//! [arbitrary]: https://crates.io/crates/arbitrary
6971
//! [examples]: https://github.com/rhysd/keybinds-rs/tree/main/examples
7072
//!
7173
#![doc = include_str!("../doc/binding_syntax.md")]
@@ -90,6 +92,9 @@ pub mod termwiz;
9092
#[cfg(feature = "winit")]
9193
pub mod winit;
9294

95+
#[cfg(feature = "arbitrary")]
96+
pub mod arbitrary;
97+
9398
pub use error::{Error, Result};
9499
pub use key::{Key, KeyInput, KeySeq, Match, Mods};
95100
pub use keybind::{Found, Keybind, KeybindDispatcher, Keybinds, DEFAULT_TIMEOUT};

src/serde.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Support for [`serde`] crate.
22
//!
3-
//! This module provides a `Deserialize` support for [`Keybinds`] to easily parse key bindings from
4-
//! a configuration file.
3+
//! This module provides a [`Deserialize`] trait support for [`Keybinds`] to easily parse key bindings
4+
//! from a configuration file.
55
//!
66
//! ```
77
//! use serde::Deserialize;

0 commit comments

Comments
 (0)