Calendar events, as a 1:1 extension of encounters with public submission workflow
| Tier | content |
| Role | unclassified (baselined) |
| Path | crates/content/calendar |
| Edition | 2021 |
| Targets | content_calendar, integration |
| Public items | 14 across 1 module |
| Tests | 37 |
What it is for
# content-calendar
Architecture
| Library crate | What It Provides |
|---|---|
domain/encounters | Core event data (title, description, location, scheduled_start/end) |
operations/workflow | Moderation pattern (ApprovalStatus: pending → approved/rejected) |
| `content/calendar` | Composition layer + calendar-specific concerns |
Key Design Decisions
1. 1:1 Extension Pattern: calendar_events uses encounter_id as its PK/FK. The table doesn't duplicate encounter fields - it only adds calendar-specific data.
2. Real FK Enforcement: submitted_by_party_id and reviewed_by_party_id are real foreign keys to the parties table (not fake "references consuming app" comments).
3. PartyId-First: Uses PartyId newtype everywhere for type safety.
4. Minimal States: Only pending, approved, rejected, cancelled. No draft state to avoid scope creep.
5. Trust Logic in Repo: Auto-approval happens in repository code, not database triggers. This keeps business logic testable and debuggable.
6. Scoped Trust: Trust is calendar-specific, not global.
Example
use content_calendar::{CalendarEvent, CalendarEventRepo, ModerationStatus};
use identity_parties::PartyId;
use uuid::Uuid;
// Submit an event (auto-approves if submitter is trusted)
let encounter_id = Uuid::new_v4();
let submitter = PartyId::new_v4();
let event = repo.submit(&pool, encounter_id, submitter).await?;
// Check status
if event.moderation_status == ModerationStatus::Pending {
// Needs review
repo.approve(&pool, encounter_id, reviewer_id, Some("Looks good!".into())).await?;
}
Capabilities
verb:approve
PostgreSQL repository functions for calendar events.
| Item |
|---|
async fn approve_event(pool : & PgPool, encounter_id : EncounterId, reviewer : PartyId, notes : Option <String>,) -> Result <(), CalendarError> |
verb:create
PostgreSQL repository functions for calendar events.
| Item |
|---|
async fn create_calendar_scope(pool : & PgPool, name : & str, slug : & str, scope_type : ScopeType, owner_party_id : Option <PartyId>, default_visibility : Visibility, submission_policy : SubmissionPolicy, moderation_policy : ModerationPolicy, require_approval : bool,) -> Result <CalendarScope, CalendarError> |
verb:get
PostgreSQL repository functions for calendar events.
| Item |
|---|
async fn get_calendar_event(pool : & PgPool, encounter_id : EncounterId,) -> Result <Option <CalendarEvent>, CalendarError> |
async fn get_submitter_trust(pool : & PgPool, party_id : PartyId,) -> Result <Option <CalendarSubmitterTrust>, CalendarError> |
async fn get_scope_by_slug(pool : & PgPool, slug : & str,) -> Result <Option <CalendarScope>, CalendarError> |
verb:is
PostgreSQL repository functions for calendar events.
| Item |
|---|
async fn is_submitter_trusted(pool : & PgPool, party_id : PartyId) -> Result <bool, CalendarError> |
verb:list
PostgreSQL repository functions for calendar events.
| Item |
|---|
async fn list_public_events(pool : & PgPool, from : DateTime <Utc>, to : DateTime <Utc>,) -> Result <Vec <CalendarEventWithEncounter>, CalendarError> |
async fn list_pending_events(pool : & PgPool,) -> Result <Vec <CalendarEventWithEncounter>, CalendarError> |
async fn list_scopes(pool : & PgPool) -> Result <Vec <CalendarScope>, CalendarError> |
async fn list_events_by_scope(pool : & PgPool, scope_id : ScopeId, from : DateTime <Utc>, to : DateTime <Utc>,) -> Result <Vec <CalendarEventWithEncounter>, CalendarError> |
verb:reject
PostgreSQL repository functions for calendar events.
| Item |
|---|
async fn reject_event(pool : & PgPool, encounter_id : EncounterId, reviewer : PartyId, notes : Option <String>,) -> Result <(), CalendarError> |
verb:set
PostgreSQL repository functions for calendar events.
| Item |
|---|
async fn set_submitter_trust(pool : & PgPool, party_id : PartyId, is_trusted : bool, granted_by : PartyId, notes : Option <String>,) -> Result <(), CalendarError> |
verb:submit
PostgreSQL repository functions for calendar events.
| Item |
|---|
async fn submit_calendar_event(pool : & PgPool, encounter_id : EncounterId, submitted_by : PartyId,) -> Result <CalendarEvent, CalendarError> |
async fn submit_event_to_scope(pool : & PgPool, encounter_id : EncounterId, submitted_by : PartyId, scope_id : ScopeId, visibility : Option <Visibility>,) -> Result <CalendarEvent, CalendarError> |
How to use it
No examples/ target and no doctest in this crate's rustdoc. The tests listed under Verification are the closest executable usage.
Module structure
content_calendar
postgres
Public surface
`postgres`
| Item | What it is |
|---|---|
async fn submit_calendar_event(pool : & PgPool, encounter_id : EncounterId, submitted_by : PartyId,) -> Result <CalendarEvent, CalendarError> | Submit a new calendar event for an existing encounter |
async fn get_calendar_event(pool : & PgPool, encounter_id : EncounterId,) -> Result <Option <CalendarEvent>, CalendarError> | Get a calendar event by its encounter ID. |
async fn list_public_events(pool : & PgPool, from : DateTime <Utc>, to : DateTime <Utc>,) -> Result <Vec <CalendarEventWithEncounter>, CalendarError> | List public approved events within a time range |
async fn list_pending_events(pool : & PgPool,) -> Result <Vec <CalendarEventWithEncounter>, CalendarError> | List all pending events awaiting moderation. |
async fn approve_event(pool : & PgPool, encounter_id : EncounterId, reviewer : PartyId, notes : Option <String>,) -> Result <(), CalendarError> | Approve a pending calendar event. |
async fn reject_event(pool : & PgPool, encounter_id : EncounterId, reviewer : PartyId, notes : Option <String>,) -> Result <(), CalendarError> | Reject a pending calendar event. |
async fn is_submitter_trusted(pool : & PgPool, party_id : PartyId) -> Result <bool, CalendarError> | Check if a party is trusted for auto-approval. |
async fn set_submitter_trust(pool : & PgPool, party_id : PartyId, is_trusted : bool, granted_by : PartyId, notes : Option <String>,) -> Result <(), CalendarError> | Grant or revoke trust for a party. |
async fn get_submitter_trust(pool : & PgPool, party_id : PartyId,) -> Result <Option <CalendarSubmitterTrust>, CalendarError> | Get the trust record for a party (if exists). |
async fn create_calendar_scope(pool : & PgPool, name : & str, slug : & str, scope_type : ScopeType, owner_party_id : Option <PartyId>, default_visibility : Visibility, submission_policy : SubmissionPolicy, moderation_policy : ModerationPolicy, require_approval : bool,) -> Result <CalendarScope, CalendarError> | Create a new calendar scope |
async fn get_scope_by_slug(pool : & PgPool, slug : & str,) -> Result <Option <CalendarScope>, CalendarError> | Get a calendar scope by its unique slug. |
async fn list_scopes(pool : & PgPool) -> Result <Vec <CalendarScope>, CalendarError> | List all calendar scopes. |
async fn submit_event_to_scope(pool : & PgPool, encounter_id : EncounterId, submitted_by : PartyId, scope_id : ScopeId, visibility : Option <Visibility>,) -> Result <CalendarEvent, CalendarError> | Submit a calendar event to a specific scope |
async fn list_events_by_scope(pool : & PgPool, scope_id : ScopeId, from : DateTime <Utc>, to : DateTime <Utc>,) -> Result <Vec <CalendarEventWithEncounter>, CalendarError> | List approved events within a scope for a time range. |
Re-exports. Exported here, defined elsewhere.
| Export | Defined in |
|---|---|
CalendarError | error::CalendarError |
PartyId | identity_parties::PartyId |
{CalendarEvent,CalendarEventWithEncounter,CalendarScope,CalendarSubmitterTrust,EncounterId,ModerationPolicy,ModerationStatus,ScopeId,ScopeType,SubmissionPolicy,Visibility,} | models::{CalendarEvent,CalendarEventWithEncounter,CalendarScope,CalendarSubmitterTrust,EncounterId,ModerationPolicy,ModerationStatus,ScopeId,ScopeType,SubmissionPolicy,Visibility,} |
{CalendarEventRepo,SubmitEventInput} | repo::{CalendarEventRepo,SubmitEventInput} |
{approve_event,create_calendar_scope,get_calendar_event,get_scope_by_slug,get_submitter_trust,is_submitter_trusted,list_events_by_scope,list_pending_events,list_public_events,list_scopes,reject_event,set_submitter_trust,submit_calendar_event,submit_event_to_scope,} | postgres::{approve_event,create_calendar_scope,get_calendar_event,get_scope_by_slug,get_submitter_trust,is_submitter_trusted,list_events_by_scope,list_pending_events,list_public_events,list_scopes,reject_event,set_submitter_trust,submit_calendar_event,submit_event_to_scope,} |
Boundary
Reaches into foundation, identity.
Shares tier content with 5 other crates: content-assets, content-calendar-recurrence, content-cms, content-directory-listing, content-notes.
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | content |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/content/calendar |
| Vocabulary in force (lexicon) | current |
Tier flow. Which tiers this crate's own edges cross.
flowchart LR n_content["content"] --> n_foundation["foundation"] n_content["content"] --> n_identity["identity"]
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `foundation-basemodels` | foundation | no | always |
| `identity-parties` | identity | no | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
async-trait | ^0.1 | — | no | always |
chrono | ^0.4 | serde | no | always |
serde | ^1 | derive | no | always |
serde_json | ^1 | — | no | always |
sqlx | ^0.8 | runtime-tokio, postgres, chrono, uuid, json | yes | always |
thiserror | ^2 | — | no | always |
uuid | ^1 | v4, serde | no | always |
Development, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
dotenvy | ^0.15 | — | no | always |
tokio | ^1 | full | no | always |
tokio-test | ^0.4 | — | no | always |
Build. None.
Depended on by. 2 workspace crates.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR n_application_calendar["application-calendar"] -->|uses| SELF n_foundation_test_support["foundation-test-support"] -->|uses| SELF SELF["content-calendar"] SELF -->|runtime| n_foundation_basemodels["foundation-basemodels"] SELF -->|runtime| n_identity_parties["identity-parties"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
| Feature | Enables | On by default |
|---|---|---|
default | — | yes |
postgres | dep:sqlx | no |
flowchart LR n_default["default"] n_postgres["postgres"] --> n_dep_sqlx["dep:sqlx"]
Targets
| Kind | Name | Source |
|---|---|---|
| lib | content_calendar | `src/lib.rs` |
| test | integration | `tests/integration.rs` |
Error model
No public error type was detected: no public item declares a type named *Error, and no public signature returns one.
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | yes |
| async runtime | none detected |
| database access | yes |
| network I/O | none detected |
| unsafe code | none detected |
| environment variables | none detected |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
No environment variable is read with a literal name anywhere in this crate. A variable whose key is computed at run time cannot be listed here, and is not claimed to be absent.
Related capabilities
2 workspace crates depend on this one: application-calendar, foundation-test-support.
Verification
| Kind | Count |
|---|---|
| Unit tests | 10 |
| Integration tests | 27 |
| Examples | 0 |
| Doctests | 0 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
postgres | 14 | 0 | 6 |
What the tests establish, by name:
test_approve_already_approved_fails—tests/integration.rstest_approve_pending_event—tests/integration.rstest_create_scope_duplicate_slug_fails—tests/integration.rstest_create_scope_org—tests/integration.rstest_create_scope_site—tests/integration.rstest_get_returns_event—tests/integration.rstest_get_returns_none_for_nonexistent—tests/integration.rstest_get_scope_by_slug—tests/integration.rstest_get_scope_by_slug_not_found—tests/integration.rstest_list_events_by_scope—tests/integration.rstest_list_pending_returns_only_pending—tests/integration.rstest_list_public_filters_by_time_range—tests/integration.rstest_list_scopes—tests/integration.rstest_moderation_policy_parsing—tests/integration.rstest_reject_pending_event—tests/integration.rstest_scope_type_parsing—tests/integration.rstest_submission_policy_parsing—tests/integration.rstest_submit_auto_approves_for_trusted—tests/integration.rstest_submit_creates_pending_for_untrusted—tests/integration.rstest_submit_event_to_scope—tests/integration.rstest_submit_event_with_custom_visibility—tests/integration.rstest_submit_fails_for_duplicate—tests/integration.rstest_submit_fails_for_nonexistent_encounter—tests/integration.rstest_trust_check_untrusted_by_default—tests/integration.rstest_trust_grant_and_check—tests/integration.rstest_trust_revoke—tests/integration.rstest_visibility_parsing—tests/integration.rstest_exports—src/lib.rstest_calendar_event_builder—src/models.rstest_calendar_event_new_auto_approved—src/models.rs- _… 7 more_
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 14 | 14 |
Public modules with a //! block | 1 | 1 |
pie showData
title Public items with rustdoc
"Documented" : 14
"No rustdoc detected" : 0
Metrics
| Metric | Value |
|---|---|
| Rust source files | 5 |
| Source lines | 1763 |
| Code lines | 1265 |
| Public API items | 14 |
| Public modules | 1 |
| Tests | 37 |
| Examples | 0 |
| Cargo features | 2 |
| Direct runtime dependencies | 9 |
| Workspace reverse dependencies | 2 |
pie showData
title Public API by kind
"function" : 14
pie showData
title Rust source composition
"Code" : 1265
"Blank or comment" : 498
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.