Holy
A proc-macro library that ships derive macros for everyday struct chores: getters, setters, observers, fuzz constructors, and input sanitization. Pulling it as a path / crates.io dep keeps each consumer free of hand-rolled boilerplate and centralizes policy changes (new sanitize rules, observer hooks, etc.) in one crate.
Available derives
Getters— auto-generatesget_<field>()accessors.Setters— auto-generatesset_<field>()mutators.Observer— emits change events on field updates.Fuzz— generatesrandom()constructors for tests.Sanitize— generates.sanitize()+ per-field.sanitize_<field>()methods driven by#[holy(sanitize = "rule1, rule2(arg)")]attributes.
Getters & Setters
let mut user = User ;
let name: &String = user.name; // getter
user.set_age; // setter
Supports generic structs:
Attributes
Control visibility and behavior per-field with #[holy(...)]:
#[holy(public)]— make the generated getter/setterpubregardless of field visibility#[holy(private)]— make the generated getter/setter private regardless of field visibility#[holy(skip)]— skip generating getter/setter for this field#[holy(observe)]— mark field for observer pattern (used withObserverderive)
Observer
Derive Observer to generate a companion struct for the observer pattern:
// Generates `SensorObservers` companion struct
let mut observers = new;
observers.add_temperature_observer;
observers.notify_temperature_observers;
Sanitize
Derive Sanitize to generate .sanitize() plus per-field
.sanitize_<field>() methods. Each rule is declared inline on the
field via #[holy(sanitize = "...")] and runs in the order written.
| Rule | Field type | Effect |
|---|---|---|
trim |
String |
.trim().to_string() |
lowercase |
String |
.to_lowercase() |
uppercase |
String |
.to_uppercase() |
alphanumeric |
String |
retain only char::is_alphanumeric() |
escape_html |
String |
replace & < > " ' with HTML entities |
nul_strip |
String |
drop every \0 byte (0.2.1) |
control_strip |
String |
drop ASCII/Unicode control chars + bidi overrides (U+202A..U+202E, U+2066..U+2069) + zero-width chars (U+200B..U+200D, U+FEFF) (0.2.1) |
slug |
String |
lowercase + ASCII alphanumerics + collapse separator runs into single - + trim leading/trailing - (0.2.1) |
truncate(N) |
String |
UTF-8-safe byte truncate to N; walks back to nearest char boundary so multi-byte codepoints never panic |
clamp(min, max) |
numeric | .clamp(min, max) |
Use control_strip only on inline text fields (titles, signatures,
slugs). It removes \n and \t so it is not appropriate for
markdown bodies — those should be rendered through a markdown sanitizer
on the read path instead.
Option<String> fields work the same way: rules run only when the
field is Some(_) and None passes through. Useful for partial-update
DTOs (e.g. pub bio: Option<String>).
After payload.sanitize() the struct is safe to forward into downstream
RPCs without per-field length / control-char checks.
Fuzz
Derive Fuzz to generate random() constructors for tests:
let c = random;