Use traits to keep event methods dry

This commit is contained in:
Jon Staab
2026-05-20 14:27:26 -07:00
parent 8ad0bc393b
commit d0709e1811
7 changed files with 138 additions and 61 deletions
+80
View File
@@ -525,6 +525,86 @@ Deserialization does not verify the signature. Parsing is cheap; `verify()`
is not, and there are many places where you want to read an event off disk
without paying for a signature check.
## A shared surface across event stages
There is a lot of overlap between various event structs. A few minimal accessor
traits capture read access to various fields so that we can define extensions
to events without duplication.
```rust {file=coracle-lib/src/events.rs}
pub trait HasTags {
fn tags(&self) -> &Tags;
}
pub trait HasId {
fn id(&self) -> &[u8; 32];
}
impl HasTags for HashedEvent {
fn tags(&self) -> &Tags {
&self.tags
}
}
impl HasTags for Event {
fn tags(&self) -> &Tags {
&self.tags
}
}
impl HasId for HashedEvent {
fn id(&self) -> &[u8; 32] {
&self.id
}
}
impl HasId for Event {
fn id(&self) -> &[u8; 32] {
&self.id
}
}
```
Subsequent chapters will add *extension traits* bounded on these accessors,
supply the method bodies as defaults, and close with a blanket impl over every
type that satisfies the bound. The logic is written once and reaches both event
types — and any event type added later — without another line per struct.
The expiration chapter's version reads like this:
```rust
pub trait EventExtensionExpiration: HasTags {
fn is_expired(&self) -> bool {
is_expired(self.tags())
}
// ...the rest of the expiration queries
}
impl<T: HasTags> EventExtensionExpiration for T {}
```
A trait's methods are only callable where the trait is in scope, so the
library gathers these extension traits in a `prelude`. One glob import brings
the whole behavior-query surface along:
```rust
use coracle_lib::prelude::*;
```
```rust {file=coracle-lib/src/lib.rs}
pub mod prelude;
```
```rust {file=coracle-lib/src/prelude.rs}
//! One-stop import for the extension traits that add behavior-tag queries to
//! events. With `coracle_lib::prelude::*` in scope, methods like
//! `is_expired`, `is_protected`, and `get_pow` become callable on
//! `HashedEvent` and `Event`.
pub use crate::events::{HasId, HasTags};
```
Each later chapter that defines an extension trait adds it to this prelude.
## What's next
The next chapter introduces kinds — the integer that decides how content