Skip to content

Commit 94477b4

Browse files
committed
Derive macro for Arbitrary
1 parent 9764a52 commit 94477b4

File tree

15 files changed

+719
-9
lines changed

15 files changed

+719
-9
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: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[workspace]
2-
members = ["chaos_theory", "chaos_theory_core", "chaos_theory_v2", "xtask"]
2+
members = ["chaos_theory", "chaos_theory_core", "chaos_theory_derive", "chaos_theory_v2", "xtask"]
33
exclude = ["chaos_theory_example", "chaos_theory_example_fuzz"]
44
resolver = "2"
55

@@ -65,9 +65,14 @@ indexmap = "2.11.4"
6565
libfuzzer-sys = "0.4.12"
6666
log = "0.4.27"
6767
ordered-float = "5.1.0"
68+
prettyplease = "0.2"
69+
proc-macro2 = "1.0"
70+
quote = "1.0"
6871
regex = "1.11.1"
6972
regex-syntax = "0.8.5"
73+
syn = { version = "2.0", default-features = false }
7074
tinyvec = "1.10.0"
7175
xshell = "0.2.6"
7276

7377
chaos_theory_core = { path = "chaos_theory_core" }
78+
chaos_theory_derive = { version = "0.2.2", path = "chaos_theory_derive" }

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ you can use to reproduce the case.
4343
- structural mutations and crossover
4444
- example-guided generation
4545
- built-in swarm testing
46-
- Macro-free, imperative API
46+
- Macro-free, imperative API (with optional derive macro for `Arbitrary`)
4747
- Zero unsafe code and zero required dependencies
4848

4949
## Documentation
@@ -55,7 +55,6 @@ https://docs.rs/chaos_theory
5555
Beta – chaos_theory works well and is useful, but does not guarantee API stability.
5656

5757
Notable gaps:
58-
- Derive macro for `Arbitrary`
5958
- Proper recursive generators
6059

6160
## Contributing

chaos_theory/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ workspace = true
2323

2424
[features]
2525
# default = ["nightly"]
26-
all = ["ecow", "hashbrown", "indexmap", "ordered_float", "regex", "tinyvec"]
26+
all = ["derive", "ecow", "hashbrown", "indexmap", "ordered_float", "regex", "tinyvec"]
27+
derive = ["dep:chaos_theory_derive"]
2728
ecow = ["dep:ecow"]
2829
hashbrown = ["dep:hashbrown"]
2930
indexmap = ["dep:indexmap"]
@@ -35,6 +36,7 @@ nightly = ["_bench"]
3536
_bench = []
3637

3738
[dependencies]
39+
chaos_theory_derive = { workspace = true, optional = true }
3840
ecow = { workspace = true, optional = true }
3941
hashbrown = { workspace = true, optional = true }
4042
indexmap = { workspace = true, optional = true }

chaos_theory/TODO.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,4 @@
3131

3232
## features
3333

34-
- derive macro
3534
- consider saving failures, at least temporarily

chaos_theory/docs/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
- Add optional `derive` feature with `#[derive(chaos_theory::Arbitrary)]` support.
6+
37
## 0.2.2 (2026-02-23)
48

59
- Make FAQ link to API items.

chaos_theory/docs/FAQ.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ chaos_theory is built around simple macro-free imperative API, has fuzzing suppo
2222
and cool tricks like built-in swarm testing and example-based data generation.
2323

2424
`proptest` is more mature and feature‑rich; chaos_theory is newer and currently
25-
lacks important features like derive macro and recursion helpers.
25+
lacks some important features like recursion helpers.
2626

2727
## How is it different from `arbitrary` plus `libfuzzer_sys`?
2828

chaos_theory/docs/guide.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,15 +234,40 @@ let city = make::string_matching("[A-Za-z '-]+", true).seeded(&cities, true);
234234
Built-in generators already have seeds pre-configured internally, so use [`seeded`][generator_seeded]
235235
only to provide seeds that are specific to your domain.
236236

237+
### Deriving `Arbitrary`
238+
239+
For most domain types, prefer derive instead of hand-writing generators.
240+
241+
Enable derive support:
242+
243+
```toml
244+
[dev-dependencies]
245+
chaos_theory = { version = "0.3", features = ["derive"] }
246+
```
247+
248+
Then derive:
249+
250+
```rust
251+
# #[cfg(feature = "derive")]
252+
#[derive(Debug, chaos_theory::Arbitrary)]
253+
struct Point {
254+
x: i32,
255+
y: i32,
256+
}
257+
```
258+
259+
Derive-generated implementations follow the same rules as hand-written ones:
260+
variant choices are structural (`select`) and `example` is threaded through fields.
261+
237262
### Writing Custom Generators
238263

239264
There are two main approaches:
240265

241266
- Use [`make::from_fn`][make_from_fn] for small generators
242267
- Implement [`Generator`][generator] directly for full control
243268

244-
Most of this becomes unnecessary once a derive macro for [`Arbitrary`][arbitrary] exists, but
245-
it is still useful for domain-specific logic.
269+
With derive support for [`Arbitrary`][arbitrary], most of this is unnecessary for plain data
270+
models, but it is still useful for domain-specific logic.
246271

247272
#### Struct-Like Types
248273

chaos_theory/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ you can use to reproduce the case.
4848
- structural mutations and crossover
4949
- example-guided generation
5050
- built-in swarm testing
51-
- Macro-free, imperative API
51+
- Macro-free, imperative API (with optional derive macro for [`Arbitrary`])
5252
- Zero unsafe code and zero required dependencies
5353
5454
# Documentation
@@ -65,6 +65,9 @@ extern crate alloc;
6565
#[cfg(all(test, feature = "_bench"))]
6666
extern crate test;
6767

68+
#[cfg(feature = "derive")]
69+
pub use chaos_theory_derive::Arbitrary;
70+
6871
use std::path::Path;
6972

7073
mod base64;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2026 Gregory Petrosyan <pgregory@pgregory.net>
2+
//
3+
// This Source Code Form is subject to the terms of the Mozilla Public
4+
// License, v. 2.0. If a copy of the MPL was not distributed with this
5+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
6+
7+
//! Integration tests for `#[derive(chaos_theory::Arbitrary)]`.
8+
9+
#![cfg(feature = "derive")]
10+
11+
use chaos_theory::{Arbitrary, Source, check};
12+
13+
include!("../../testdata/derive_cases.rs");
14+
15+
fn assert_reconstruct<T>(src: &mut Source, example_label: &str, value_label: &str)
16+
where
17+
T: chaos_theory::Arbitrary + core::fmt::Debug + PartialEq,
18+
{
19+
let example: T = src.any(example_label);
20+
let value = src.as_raw().any(value_label, Some(&example));
21+
assert_eq!(value, example);
22+
}
23+
24+
#[test]
25+
fn derive_reconstructs_examples() {
26+
check(|src| {
27+
assert_reconstruct::<Point>(src, "point_example", "point_value");
28+
assert_reconstruct::<Triple>(src, "triple_example", "triple_value");
29+
assert_reconstruct::<Marker>(src, "marker_example", "marker_value");
30+
assert_reconstruct::<Imported>(src, "imported_example", "imported_value");
31+
assert_reconstruct::<Wrapper<u16>>(src, "wrapper_example", "wrapper_value");
32+
assert_reconstruct::<Action<u8>>(src, "action_example", "action_value");
33+
});
34+
}

0 commit comments

Comments
 (0)