chore: scaffold moments workspace
Cargo workspace with five crates per architecture conventions:
- moments-entities: Source enum, Event, EventQuery, SourceSummary
- moments-core: EventReader / EventWriter ports
- moments-data: PgStore (sqlx postgres adapter) + 0001_init.sql
- moments-api: axum binary; /v1/{healthz,events,sources}
- moments-worker: skeleton; pollers land in step 2
Sources committed-to for ingestion: github, gitea, hg, bugzilla.
Workstation events explicitly retired (not deferred).
Build + clippy clean. sqlx queries use the runtime API for now;
will switch to compile-time-checked macros + .sqlx offline cache
once magrathea has the moments_{ro,rw} roles and database created.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
13
crates/moments-entities/Cargo.toml
Normal file
13
crates/moments-entities/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "moments-entities"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
[dependencies]
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
chrono.workspace = true
|
||||
thiserror.workspace = true
|
||||
76
crates/moments-entities/src/lib.rs
Normal file
76
crates/moments-entities/src/lib.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Source {
|
||||
Github,
|
||||
Gitea,
|
||||
Hg,
|
||||
Bugzilla,
|
||||
}
|
||||
|
||||
impl Source {
|
||||
pub const ALL: &'static [Source] = &[
|
||||
Source::Github,
|
||||
Source::Gitea,
|
||||
Source::Hg,
|
||||
Source::Bugzilla,
|
||||
];
|
||||
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Source::Github => "github",
|
||||
Source::Gitea => "gitea",
|
||||
Source::Hg => "hg",
|
||||
Source::Bugzilla => "bugzilla",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Source {
|
||||
type Err = ParseSourceError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"github" => Ok(Source::Github),
|
||||
"gitea" => Ok(Source::Gitea),
|
||||
"hg" => Ok(Source::Hg),
|
||||
"bugzilla" => Ok(Source::Bugzilla),
|
||||
other => Err(ParseSourceError(other.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("unknown source: {0}")]
|
||||
pub struct ParseSourceError(pub String);
|
||||
|
||||
/// Raw event as stored. The presentation reshape lives in `moments-core`
|
||||
/// and runs at API request time.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Event {
|
||||
pub id: String,
|
||||
pub source: Source,
|
||||
pub action: String,
|
||||
pub occurred_at: DateTime<Utc>,
|
||||
pub payload: serde_json::Value,
|
||||
}
|
||||
|
||||
/// Filters accepted by `GET /v1/events`.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct EventQuery {
|
||||
pub from: Option<DateTime<Utc>>,
|
||||
pub to: Option<DateTime<Utc>>,
|
||||
pub sources: Option<Vec<Source>>,
|
||||
pub limit: u32,
|
||||
}
|
||||
|
||||
/// Per-source rollup returned by `GET /v1/sources`.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SourceSummary {
|
||||
pub source: Source,
|
||||
pub count: i64,
|
||||
pub earliest: Option<DateTime<Utc>>,
|
||||
pub latest: Option<DateTime<Utc>>,
|
||||
}
|
||||
Reference in New Issue
Block a user