Skip to content

Commit 6a8651a

Browse files
Squashed commit.
1 parent 9899577 commit 6a8651a

16 files changed

Lines changed: 914 additions & 234 deletions

File tree

.github/workflows/rust.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Rust
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
pull_request:
7+
branches: [ "main" ]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
12+
jobs:
13+
build:
14+
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
- name: Build
20+
run: cargo build --all-features --verbose
21+
- name: Run tests
22+
run: cargo test --all-features --verbose

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@ include = ["src/**/*", "README.md", "LICENSE*", "Cargo.toml"]
1414
features = ["debug-tools", "method-api"]
1515

1616
[dependencies]
17+
quantor_fnmeta = { path = "fnmeta" } # internal crate
1718

1819
[features]
1920
default = []
2021
debug-tools = []
21-
method-api = []
22+
method-api = []
23+
24+
[workspace]
25+
members = ["fnmeta"]

fnmeta/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "quantor_fnmeta"
3+
description = "Internal macro for advanced errors."
4+
version = "1.0.0"
5+
edition = "2021"
6+
publish = false
7+
8+
[lib]
9+
proc-macro = true

fnmeta/src/lib.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#![doc(hidden)]
2+
//! Internal procedural macros for `quantor`.
3+
//!
4+
//! Provides `#[function_name]`, which injects a local `const FUNCTION_NAME: &str`
5+
//! into any function or method, containing the function’s name as a string.
6+
//!
7+
//! Not intended for public use; this crate is private and excluded from docs.
8+
//!
9+
//! ## Example
10+
//! ```ignore
11+
//! #[function_name]
12+
//! fn run() {
13+
//! println!("{}", FUNCTION_NAME);
14+
//! }
15+
//! ```
16+
17+
use proc_macro::{TokenStream, TokenTree, Group, Delimiter};
18+
19+
#[doc(hidden)]
20+
/// Injects a local `FUNCTION_NAME: &str` constant with the name of the current function.
21+
///
22+
/// Works on both free functions and `impl` methods. Zero runtime cost.
23+
/// Intended for internal diagnostics, logging, and tracing.
24+
///
25+
/// ## Example
26+
/// ```ignore
27+
/// #[function_name]
28+
/// fn compute() {
29+
/// println!("This is '{}'", FUNCTION_NAME);
30+
/// }
31+
/// ```
32+
///
33+
/// This macro is internal to the `quantor` crate and not publicly exported.
34+
#[proc_macro_attribute]
35+
pub fn function_name(_attr: TokenStream, input: TokenStream) -> TokenStream {
36+
let mut tokens = input.into_iter();
37+
let mut output = Vec::new();
38+
39+
while let Some(token) = tokens.next() {
40+
match &token {
41+
TokenTree::Ident(ident) if ident.to_string() == "fn" => {
42+
output.push(token.clone()); // push 'fn'
43+
44+
if let Some(TokenTree::Ident(name)) = tokens.next() {
45+
let fn_name_str = name.to_string();
46+
output.push(TokenTree::Ident(name)); // push function name
47+
48+
// Copy everything until `{`
49+
while let Some(next_token) = tokens.next() {
50+
match &next_token {
51+
TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => {
52+
let original_body = group.stream();
53+
let injected = format!(
54+
"const FUNCTION_NAME: &str = \"{}\"; {}",
55+
fn_name_str,
56+
original_body
57+
);
58+
let new_body = injected.parse::<TokenStream>().unwrap();
59+
let new_group = Group::new(Delimiter::Brace, new_body);
60+
output.push(TokenTree::Group(new_group));
61+
break;
62+
}
63+
TokenTree::Group(group) => {
64+
output.push(TokenTree::Group(Group::new(
65+
group.delimiter(),
66+
group.stream(),
67+
)));
68+
}
69+
_ => output.push(next_token),
70+
}
71+
}
72+
continue;
73+
}
74+
}
75+
_ => output.push(token),
76+
}
77+
}
78+
79+
TokenStream::from_iter(output)
80+
}

readme.md

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,73 @@
11
# quantor
22
[![Crates.io](https://img.shields.io/crates/v/quantor.svg)](https://crates.io/crates/quantor)
33
[![Docs.rs](https://docs.rs/quantor/badge.svg)](https://docs.rs/quantor)
4-
[![License](https://img.shields.io/crates/l/quantor)](https://crates.io/crates/quantor)
54
[![Downloads](https://img.shields.io/crates/d/quantor.svg)](https://crates.io/crates/quantor)
6-
[![MSRV](https://img.shields.io/badge/MSRV-1.61+-blue)](https://github.com/nervousnullptr/quantor#msrv)
5+
[![MSRV](https://img.shields.io/badge/MSRV-1.58.1+-blue)](https://github.com/nervousnullptr/quantor#msrv)
76

8-
**Declarative quantifiers and logical assertions for Rust iterators and collections.**
9-
`quantor` provides lightweight, expressive tools for validation, filtering, and testing — with zero dependencies.
7+
**Declarative logic for iterators and collections.**
8+
`quantor` lets you express conditions like `forall`, `exists`, `none`, and `existsforall` directly over data — making filtering, validation, and testing expressive, readable, and idiomatic.
109

11-
---
10+
## Why Quantor?
1211

13-
## ✨ Features
14-
`quantor` lets you express logic over data in a way that feels natural and readable:
12+
Rust's iterator methods are powerful, but when you want to write logic that reads like:
1513

16-
- 📐 **Quantifiers** — Use familiar constructs like `forall`, `exists`, `none`, and `exactly_one`.
17-
- 🧹 **Selection utilities** — Filter with `select_where`, extract duplicates, check for uniqueness.
18-
- 🧠 **Structured logic** — Run `pairwise` comparisons or validate equality across items.
19-
- 🧪 **Assertions** — Add runtime guarantees with `assert_forall!`, `assert_exists!`, etc.
14+
- "All elements are even"
15+
- "At least one user is active"
16+
- "Exactly three items matched a condition"
2017

21-
---
18+
you're often stuck with `.all()`, `.any()`, `.filter().count()`, and some `assert!` noise.
2219

23-
## 🚀 Example
20+
With `quantor`, your code becomes declarative and reflects the logic you care about, not the mechanics.
21+
22+
## Highlights
23+
24+
- **Quantifiers**`forall`, `exists`, `none`, `exactly_one`, `exactly_n`, `all_equal`, `pairwise`, `forallexists`, and more.
25+
- **Assertions** — Runtime logic assertions like `assert_forall!`, `assert_unique!`, `assert_pairwise!`, with expressive failure output.
26+
- **Predicate-based selection** — Filter, deduplicate, or extract based on logic: `select_where`, `select_unique`, `select_duplicates`.
27+
- **Diagnostics** — Inspect failing indices, collect mismatches, or integrate with fuzzing tools using `QuantorError`.
28+
- **Rust-native, ergonomic API** – Works with any `IntoIterator`, zero dependencies, and optional `.method()` trait extension.
29+
30+
## Example
31+
32+
If you're validating input for a product catalog:
2433

2534
```rust
26-
use quantor::{forall, select_where, assert_forall};
35+
use quantor::{forall, exists, pairwise, assert_unique};
2736

28-
let nums = vec![2, 4, 6];
37+
#[derive(Debug)]
38+
struct Product {
39+
id: u32,
40+
price: f64,
41+
active: bool,
42+
}
2943

30-
// Check if all elements are even
31-
assert!(forall(&nums, |x| x % 2 == 0));
44+
let products = vec![
45+
Product { id: 1, price: 19.99, active: true },
46+
Product { id: 2, price: 29.99, active: true },
47+
Product { id: 3, price: 0.0, active: false },
48+
];
3249

33-
// Use the macro version for test-friendly assertions
34-
assert_forall!(&nums, |x| x % 2 == 0);
50+
// Check that all active products have a price > 0.
51+
forall(&products, |p| !p.active || p.price > 0.0)?;
3552

36-
// Extract matching elements
37-
let evens = select_where(&nums, |x| x % 2 == 0);
38-
assert_eq!(evens, vec![&2, &4, &6]);
53+
// Ensure IDs are unique.
54+
assert_unique!(&products.iter().map(|p| p.id).collect::<Vec<_>>());
55+
56+
// Confirm at least one product is available.
57+
exists(&products, |p| p.active)?;
3958
```
40-
---
4159

42-
## 📦 Installation
43-
Add this to your `Cargo.toml`:
60+
This is readable, declarative, and robust – and every check returns a Result with index-level error diagnostics.
61+
62+
## Installation
63+
Add `quantor` to your `Cargo.toml`:
4464
```
4565
quantor = "0.1"
4666
```
4767
Optional features:
48-
* `method-api` – Enable `.forall()` and other iterator-style methods.
49-
* `debug-tools` – Add debugging macros like `debug_assert_forall!` or `debug_exists!`.
50-
51-
---
68+
* `method-api` — Enables `.forall()`, `.exists()`, `.select_where()`, etc. on slices and iterators.
69+
* `debug-tools` — Enables `debug_assert_*` and `debug_*` macros for non-panicking diagnostics.
5270

5371
## 📚 Documentation
5472

55-
See docs.rs/quantor for full API documentation and examples.
73+
See [docs.rs](https://docs.rs/quantor) for full API documentation and examples.

0 commit comments

Comments
 (0)