Skip to main content

chronicle/
model.rs

1//! Core data model: entity types, relationships, and graph structure.
2
3use serde::{Deserialize, Serialize};
4
5// ── Entity ID ──────────────────────────────────────────────
6
7/// Stable string identifier for any entity in the graph.
8pub type EntityId = String;
9
10// ── Time ───────────────────────────────────────────────────
11
12/// A span of time. Both bounds are inclusive integer years.
13/// For point events, start == end.
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
15pub struct TimeSpan {
16    /// First year of the span (inclusive).
17    pub start: i32,
18    /// Last year of the span (inclusive).
19    pub end: i32,
20}
21
22// ── Status ─────────────────────────────────────────────────
23
24/// Current state of an entity, potentially changed by events.
25#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
26pub enum Status {
27    /// Entity is alive / operational / intact.
28    Active,
29    /// Character has died (terminal by default).
30    Dead,
31    /// Place has been destroyed (terminal by default).
32    Destroyed,
33    /// Faction or organization has been dissolved (terminal by default).
34    Dissolved,
35    /// Place has been evacuated but still exists.
36    Evacuated,
37    /// Entity has been captured by another force.
38    Captured,
39    /// Entity has fundamentally changed form.
40    Transformed,
41    /// Entity has been corrupted or tainted.
42    Corrupted,
43    /// Status is not yet established.
44    Unknown,
45}
46
47// ── Sentiment ──────────────────────────────────────────────
48
49/// How a participant feels about an event they were involved in.
50#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
51pub enum Sentiment {
52    /// Victorious elation.
53    Triumphant,
54    /// Justified by the outcome.
55    Vindicated,
56    /// Fulfilled obligation without strong emotion.
57    Dutiful,
58    /// Resolved to continue despite difficulty.
59    Determined,
60    /// Morally certain of the cause.
61    Righteous,
62    /// Fundamentally changed by the experience.
63    Transformative,
64    /// No strong feeling either way.
65    Neutral,
66    /// Grief or mourning.
67    Sorrowful,
68    /// Overcome by fear.
69    Fearful,
70    /// Acting out of last-resort urgency.
71    Desperate,
72    /// Resisting despite the odds.
73    Defiant,
74    /// Emotionally shattered by the event.
75    Devastating,
76    /// Bitter anger toward the outcome or participants.
77    Resentful,
78    /// Internally divided or broken by the event.
79    Fractured,
80}
81
82// ── Participant Role ───────────────────────────────────────
83
84/// The role an actor plays in an event.
85#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
86pub enum Role {
87    /// Initiated or led an offensive action.
88    Attacker,
89    /// Resisted or protected against an action.
90    Defender,
91    /// Commanded or directed the event.
92    Leader,
93    /// Took part without a specialized role.
94    Participant,
95    /// Observed without direct involvement.
96    Witness,
97    /// Made a discovery or revelation.
98    Discoverer,
99    /// Provoked or triggered the event.
100    Instigator,
101    /// Suffered harm as a result of the event.
102    Victim,
103    /// Attempted to resolve conflict between parties.
104    Mediator,
105}
106
107// ── Fidelity ───────────────────────────────────────────────
108
109/// How closely a subjective account matches the objective graph.
110#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
111pub enum Fidelity {
112    /// Matches objective graph exactly (e.g., archive-drone with intact records).
113    Canonical,
114    /// Omissions but no fabrication (e.g., eyewitness with incomplete view).
115    Partial,
116    /// Genuine misremembering (e.g., elderly NPC).
117    Distorted,
118    /// Deliberate spin, selective truth (e.g., faction propagandist).
119    Biased,
120    /// Intentional lies (e.g., trickster NPC, corrupted record).
121    Fabricated,
122    /// Damaged/degraded record (e.g., storm-damaged archive-drone).
123    Corrupted,
124}
125
126// ── State Change ───────────────────────────────────────────
127
128/// A change to an entity's state caused by an event.
129#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
130pub enum StateChange {
131    /// Entity transitions to a new [`Status`].
132    StatusChange(Status),
133    /// Entity is newly created or established.
134    Founded,
135    /// Entity gains an affiliation with the referenced entity.
136    AffiliationAdded(EntityId),
137    /// Entity loses an affiliation with the referenced entity.
138    AffiliationRemoved(EntityId),
139}
140
141// ── Event Participant ──────────────────────────────────────
142
143/// An actor's participation in an event, with role and emotional response.
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct Participant {
146    /// The actor who participated.
147    pub actor: EntityId,
148    /// What role the actor played.
149    pub role: Role,
150    /// How the actor felt about the event.
151    pub sentiment: Sentiment,
152}
153
154// ── Event State Change Record ──────────────────────────────
155
156/// Links a [`StateChange`] to the entity it affects within an event.
157#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct EventStateChange {
159    /// The entity whose state changed.
160    pub entity: EntityId,
161    /// The change that occurred.
162    pub change: StateChange,
163}
164
165// ── Entity Types ───────────────────────────────────────────
166
167/// Classification of an [`Actor`].
168#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
169pub enum ActorType {
170    /// An individual person or named character.
171    Character,
172    /// A political, military, or ideological group.
173    Faction,
174    /// A divine or supernatural being.
175    Deity,
176    /// A structured group (guild, company, order).
177    Organization,
178    /// A non-humanoid living entity.
179    Creature,
180}
181
182/// Classification of a [`Place`].
183#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
184pub enum PlaceType {
185    /// A city, town, or village.
186    Settlement,
187    /// A broad geographic area containing other places.
188    Region,
189    /// An underground or enclosed adventure site.
190    Dungeon,
191    /// A notable geographic or constructed feature.
192    Landmark,
193    /// A destroyed or abandoned former settlement.
194    Ruin,
195}
196
197/// Classification of an [`Event`].
198#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
199pub enum EventType {
200    /// Open armed conflict.
201    Battle,
202    /// Prolonged encirclement or assault on a fortified place.
203    Siege,
204    /// Revelation of new knowledge or territory.
205    Discovery,
206    /// Governance, diplomacy, or power-structure change.
207    Political,
208    /// Large-scale movement of people.
209    Migration,
210    /// The death of a significant entity.
211    Death,
212    /// Establishment of a new settlement, faction, or institution.
213    Founding,
214    /// A large-scale disaster (natural or otherwise).
215    Catastrophe,
216    /// A magical, religious, or ceremonial event.
217    Ritual,
218}
219
220/// Classification of a [`Concept`].
221#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
222pub enum ConceptType {
223    /// A belief system or faith.
224    Religion,
225    /// A technique, invention, or body of knowledge.
226    Technology,
227    /// A unique or significant object.
228    Artifact,
229    /// A codified rule or legal framework.
230    Law,
231    /// A cultural practice or custom.
232    Tradition,
233}
234
235// ── Entities (RON-deserializable) ──────────────────────────
236
237/// A character, faction, deity, organization, or creature.
238#[derive(Debug, Clone, Serialize, Deserialize)]
239pub struct Actor {
240    /// Unique identifier for this actor.
241    pub id: EntityId,
242    /// Display name.
243    pub name: String,
244    /// What kind of actor this is.
245    #[serde(rename = "type")]
246    pub actor_type: ActorType,
247    /// Current status (defaults to [`Status::Active`]).
248    #[serde(default = "default_status")]
249    pub status: Status,
250    /// The event that caused the current status, if any.
251    #[serde(default)]
252    pub status_since_event: Option<EntityId>,
253    /// Factions or organizations this actor belongs to.
254    #[serde(default)]
255    pub affiliations: Vec<EntityId>,
256    /// Birth-to-death (or founding-to-dissolution) time span.
257    #[serde(default)]
258    pub lifespan: Option<TimeSpan>,
259    /// Free-text description.
260    #[serde(default)]
261    pub description: String,
262}
263
264/// A settlement, region, dungeon, landmark, or ruin.
265#[derive(Debug, Clone, Serialize, Deserialize)]
266pub struct Place {
267    /// Unique identifier for this place.
268    pub id: EntityId,
269    /// Display name.
270    pub name: String,
271    /// What kind of place this is.
272    #[serde(rename = "type")]
273    pub place_type: PlaceType,
274    /// Current status (defaults to [`Status::Active`]).
275    #[serde(default = "default_status")]
276    pub status: Status,
277    /// The event that caused the current status, if any.
278    #[serde(default)]
279    pub status_since_event: Option<EntityId>,
280    /// Parent region this place is located within.
281    #[serde(default)]
282    pub region: Option<EntityId>,
283    /// Free-text description.
284    #[serde(default)]
285    pub description: String,
286}
287
288/// Something that happened: a battle, discovery, political shift, etc.
289#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct Event {
291    /// Unique identifier for this event.
292    pub id: EntityId,
293    /// Display name.
294    pub name: String,
295    /// What kind of event this is.
296    #[serde(rename = "type")]
297    pub event_type: EventType,
298    /// When the event occurred (inclusive year range).
299    pub time_span: TimeSpan,
300    /// Where the event took place, if applicable.
301    #[serde(default)]
302    pub location: Option<EntityId>,
303    /// Events that caused or led to this one.
304    #[serde(default)]
305    pub caused_by: Vec<EntityId>,
306    /// Actors involved and their roles.
307    #[serde(default)]
308    pub participants: Vec<Participant>,
309    /// State changes this event inflicted on entities.
310    #[serde(default)]
311    pub state_changes: Vec<EventStateChange>,
312    /// Free-text description.
313    #[serde(default)]
314    pub description: String,
315}
316
317/// An abstract idea: religion, technology, artifact, law, or tradition.
318#[derive(Debug, Clone, Serialize, Deserialize)]
319pub struct Concept {
320    /// Unique identifier for this concept.
321    pub id: EntityId,
322    /// Display name.
323    pub name: String,
324    /// What kind of concept this is.
325    #[serde(rename = "type")]
326    pub concept_type: ConceptType,
327    /// The event that introduced this concept, if any.
328    #[serde(default)]
329    pub origin_event: Option<EntityId>,
330    /// Free-text description.
331    #[serde(default)]
332    pub description: String,
333}
334
335/// A subjective narrative fragment attributed to a source actor.
336#[derive(Debug, Clone, Serialize, Deserialize)]
337pub struct Account {
338    /// Unique identifier for this account.
339    pub id: EntityId,
340    /// The actor who authored or narrated this account.
341    pub source: EntityId,
342    /// How reliable this account is relative to the objective graph.
343    pub fidelity: Fidelity,
344    /// Events this account describes or references.
345    #[serde(default)]
346    pub event_refs: Vec<EntityId>,
347    /// The narrative text, potentially containing `{entity_id}` references.
348    pub text: String,
349}
350
351fn default_status() -> Status {
352    Status::Active
353}
354
355// ── Validation Config ──────────────────────────────────────
356
357/// Policy configuration for validation rules.
358#[derive(Debug, Clone)]
359pub struct ValidationConfig {
360    /// Statuses that prevent an entity from participating in future events.
361    pub terminal_statuses: std::collections::HashSet<Status>,
362}
363
364impl Default for ValidationConfig {
365    fn default() -> Self {
366        Self {
367            terminal_statuses: [Status::Dead, Status::Destroyed, Status::Dissolved].into(),
368        }
369    }
370}
371
372// ── Graph Node / Edge Enums ────────────────────────────────
373
374/// A node in the chronicle graph.
375#[derive(Debug, Clone, Serialize, Deserialize)]
376pub enum Entity {
377    /// An actor node (character, faction, etc.).
378    Actor(Actor),
379    /// A place node (settlement, region, etc.).
380    Place(Place),
381    /// An event node (battle, discovery, etc.).
382    Event(Event),
383    /// A concept node (religion, artifact, etc.).
384    Concept(Concept),
385    /// A subjective account node.
386    Account(Account),
387}
388
389impl Entity {
390    /// Returns the [`EntityId`] of the wrapped entity.
391    pub fn id(&self) -> &str {
392        match self {
393            Entity::Actor(a) => &a.id,
394            Entity::Place(p) => &p.id,
395            Entity::Event(e) => &e.id,
396            Entity::Concept(c) => &c.id,
397            Entity::Account(a) => &a.id,
398        }
399    }
400}
401
402/// An edge in the chronicle graph.
403#[derive(Debug, Clone, Serialize, Deserialize)]
404pub enum Relationship {
405    /// Actor → Event
406    ParticipatedIn {
407        /// The role the actor played in the event.
408        role: Role,
409        /// How the actor felt about the event.
410        sentiment: Sentiment,
411    },
412    /// Event → Place
413    OccurredAt,
414    /// Event → Event (effect → cause)
415    CausedBy,
416    /// Event → affected Entity
417    HasStateChange {
418        /// The state change that was applied.
419        change: StateChange,
420    },
421    /// Actor → Actor (member → faction)
422    AffiliatedWith,
423    /// Concept → Event
424    OriginatedFrom,
425    /// Account → Event
426    AccountOf,
427    /// Account → Actor (source)
428    AuthoredBy,
429    /// Account → any Entity (from {entity_id} refs in text)
430    Mentions,
431    /// Place → Place (child → parent region)
432    LocatedIn,
433}