postmortem 0.1.1

A validation library that accumulates all errors for comprehensive feedback
Documentation
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
---
number: 15
title: Performance and Polish
category: optimization
priority: low
status: draft
dependencies: [1, 2, 3, 4, 5, 6, 7, 8, 9]
created: 2025-11-26
---

# Specification 015: Performance and Polish

**Category**: optimization
**Priority**: low
**Status**: draft
**Dependencies**: All previous specs (001-014)

## Context

Before release, postmortem needs performance validation, API polish, and final documentation review. This specification covers benchmarking, optimization opportunities, ergonomic improvements, and release preparation.

## Objective

Ensure postmortem is production-ready:
1. Benchmark validation performance
2. Optimize hot paths
3. Polish API ergonomics
4. Complete documentation
5. Prepare for v0.1 release

## Requirements

### Functional Requirements

1. **Performance Benchmarks**
   - Benchmark string validation
   - Benchmark object validation (various sizes)
   - Benchmark array validation
   - Benchmark nested structures
   - Benchmark schema compilation (if applicable)

2. **Optimizations**
   - Lazy regex compilation (compile on first use)
   - Schema compilation (pre-compute validator chain)
   - Reduce allocations in hot paths
   - Efficient path building

3. **API Polish**
   - Review builder method ergonomics
   - Improve type inference where possible
   - Consistent naming conventions
   - IDE-friendly API design

4. **Documentation**
   - Complete rustdoc coverage
   - User guide document
   - Migration guide (from other validation libs)
   - CHANGELOG for release

5. **Release Preparation**
   - Version 0.1.0 prep
   - Cargo.toml metadata
   - CI/CD configuration
   - License and README

### Non-Functional Requirements

- Validation of 1000-field object < 1ms
- Minimal allocations per validation
- Consistent API patterns throughout
- Documentation builds without warnings

## Acceptance Criteria

- [ ] Benchmarks exist for all schema types
- [ ] Regex compiled lazily (once per schema)
- [ ] Path building uses efficient allocation
- [ ] All public items have rustdoc
- [ ] User guide covers common use cases
- [ ] CHANGELOG documents all features
- [ ] CI runs tests, clippy, fmt
- [ ] Version 0.1.0 ready to publish

## Technical Details

### Implementation Approach

```rust
// Benchmark structure
// benches/validation.rs

use criterion::{criterion_group, criterion_main, Criterion, BenchmarkId};
use postmortem::{Schema, JsonPath};
use serde_json::json;

fn string_validation_benchmark(c: &mut Criterion) {
    let schema = Schema::string()
        .min_len(1)
        .max_len(100)
        .pattern(r"^[a-zA-Z0-9_]+$").unwrap();

    let valid_string = json!("valid_username_123");
    let path = JsonPath::root();

    c.bench_function("string_validation", |b| {
        b.iter(|| schema.validate(&valid_string, &path))
    });
}

fn object_validation_benchmark(c: &mut Criterion) {
    let mut group = c.benchmark_group("object_validation");

    for field_count in [10, 100, 1000].iter() {
        let schema = build_object_schema(*field_count);
        let value = build_object_value(*field_count);
        let path = JsonPath::root();

        group.bench_with_input(
            BenchmarkId::from_parameter(field_count),
            field_count,
            |b, _| b.iter(|| schema.validate(&value, &path)),
        );
    }

    group.finish();
}

fn nested_object_benchmark(c: &mut Criterion) {
    let mut group = c.benchmark_group("nested_object");

    for depth in [1, 5, 10, 20].iter() {
        let schema = build_nested_schema(*depth);
        let value = build_nested_value(*depth);
        let path = JsonPath::root();

        group.bench_with_input(
            BenchmarkId::from_parameter(depth),
            depth,
            |b, _| b.iter(|| schema.validate(&value, &path)),
        );
    }

    group.finish();
}

fn array_validation_benchmark(c: &mut Criterion) {
    let mut group = c.benchmark_group("array_validation");

    for item_count in [10, 100, 1000].iter() {
        let schema = Schema::array(Schema::integer().positive());
        let value: Vec<_> = (1..=*item_count).collect();
        let json_value = json!(value);
        let path = JsonPath::root();

        group.bench_with_input(
            BenchmarkId::from_parameter(item_count),
            item_count,
            |b, _| b.iter(|| schema.validate(&json_value, &path)),
        );
    }

    group.finish();
}

criterion_group!(
    benches,
    string_validation_benchmark,
    object_validation_benchmark,
    nested_object_benchmark,
    array_validation_benchmark,
);
criterion_main!(benches);

// Lazy regex compilation
use once_cell::sync::Lazy;
use regex::Regex;

pub struct LazyRegex {
    pattern: String,
    compiled: Lazy<Result<Regex, regex::Error>>,
}

impl LazyRegex {
    pub fn new(pattern: impl Into<String>) -> Self {
        let pattern = pattern.into();
        let pattern_clone = pattern.clone();
        Self {
            pattern,
            compiled: Lazy::new(move || Regex::new(&pattern_clone)),
        }
    }

    pub fn is_match(&self, text: &str) -> Result<bool, &regex::Error> {
        match &*self.compiled {
            Ok(re) => Ok(re.is_match(text)),
            Err(e) => Err(e),
        }
    }
}

// Efficient path building
#[derive(Clone)]
pub struct JsonPath {
    // Use small string optimization or interning
    segments: SmallVec<[PathSegment; 8]>,
}

impl JsonPath {
    pub fn push_field(&self, name: &str) -> Self {
        let mut segments = self.segments.clone();
        segments.push(PathSegment::Field(name.into()));
        Self { segments }
    }

    // Use a cached string for display
    pub fn to_string_cached(&self) -> &str {
        // Cache the formatted string
        // ...
    }
}

// Schema compilation (optional optimization)
pub struct CompiledSchema {
    validators: Vec<Box<dyn Validator>>,
}

impl StringSchema {
    /// Compile schema for repeated validation
    pub fn compile(&self) -> CompiledSchema {
        let validators: Vec<Box<dyn Validator>> = self.constraints
            .iter()
            .map(|c| c.to_validator())
            .collect();

        CompiledSchema { validators }
    }
}

impl CompiledSchema {
    pub fn validate(&self, value: &Value, path: &JsonPath) -> Validation<ValidatedValue, SchemaErrors> {
        // Fast path: run pre-compiled validators
        let mut errors = Vec::new();

        for validator in &self.validators {
            if let Err(e) = validator.validate(value, path) {
                errors.push(e);
            }
        }

        // ...
    }
}
```

### Performance Targets

| Operation | Target | Notes |
|-----------|--------|-------|
| Simple string validation | < 100ns | min_len, max_len |
| String with regex | < 1μs | Compiled regex |
| Object (10 fields) | < 10μs | All string fields |
| Object (100 fields) | < 100μs | All string fields |
| Object (1000 fields) | < 1ms | All string fields |
| Array (1000 items) | < 500μs | Integer items |
| Nested (depth 10) | < 50μs | Small objects |

### API Polish Checklist

- [ ] Builder methods return `Self` consistently
- [ ] Error messages are clear and actionable
- [ ] Type parameters have good defaults
- [ ] Methods that can fail use `Result`
- [ ] Trait bounds are minimal
- [ ] Public types implement common traits (Debug, Clone, etc.)
- [ ] No clippy warnings
- [ ] No rustfmt issues

### Architecture Changes

- Add `benches/` directory for criterion benchmarks
- Add schema compilation option
- Optimize path building

### Data Structures

- `LazyRegex` for lazy compilation
- `CompiledSchema` for pre-compiled validation
- Optimized `JsonPath` with SmallVec

### APIs and Interfaces

```rust
// Schema compilation (optional)
StringSchema::compile(&self) -> CompiledSchema
CompiledSchema::validate(&self, value: &Value, path: &JsonPath) -> Validation<...>

// No new public APIs, focus on optimization of existing
```

## Dependencies

- **Prerequisites**: All previous specs
- **Affected Components**: All schema types
- **External Dependencies**:
  - `criterion` for benchmarking
  - `once_cell` for lazy initialization
  - `smallvec` for small vector optimization

## Testing Strategy

- **Benchmarks**:
  - All schema types benchmarked
  - Various input sizes tested
  - Comparison before/after optimization

- **Regression Tests**:
  - Ensure optimizations don't break correctness
  - Test edge cases

## Documentation Requirements

- **Code Documentation**: Complete rustdoc
- **User Documentation**:
  - Getting started guide
  - Schema reference
  - Best practices
- **Architecture Updates**: Performance notes

## Implementation Notes

- Use `#[inline]` judiciously for hot paths
- Consider `#[cold]` for error paths
- Profile before optimizing
- Document any breaking changes

## Migration and Compatibility

This is the final polish stage. Any API changes should be documented in CHANGELOG.

## Files to Create/Modify

```
benches/validation.rs
docs/guide/README.md
docs/guide/getting-started.md
docs/guide/schema-reference.md
docs/guide/best-practices.md
CHANGELOG.md
README.md (update)
Cargo.toml (metadata)
```

## Release Checklist

```markdown
## v0.1.0 Release Checklist

### Code Quality
- [ ] All tests pass
- [ ] No clippy warnings
- [ ] Code formatted with rustfmt
- [ ] Benchmarks show acceptable performance

### Documentation
- [ ] All public items have rustdoc
- [ ] Examples compile and run
- [ ] README is complete
- [ ] CHANGELOG lists all features

### Metadata
- [ ] Cargo.toml has correct metadata
- [ ] License file present
- [ ] Repository link correct

### Final Steps
- [ ] Create git tag v0.1.0
- [ ] Publish to crates.io
- [ ] Announce release
```

## Cargo.toml Metadata

```toml
[package]
name = "postmortem"
version = "0.1.0"
edition = "2021"
rust-version = "1.70"
description = "Runtime schema validation with comprehensive error accumulation"
license = "MIT OR Apache-2.0"
repository = "https://github.com/memento-mori/postmortem"
documentation = "https://docs.rs/postmortem"
readme = "README.md"
keywords = ["validation", "schema", "json", "api"]
categories = ["data-structures", "web-programming"]

[badges]
maintenance = { status = "actively-developed" }
```

## Example Usage

```rust
// Performance-critical validation
let schema = Schema::object()
    .field("id", Schema::integer().positive())
    .field("email", Schema::string().email())
    .compile();  // Pre-compile for repeated use

// Validate many items efficiently
for item in items {
    let result = schema.validate(&item, &JsonPath::root());
    // ...
}
```