use crate::data_value::{MetricPrimitiveKind, RegexToMetricPrimitiveMatcher}; use crate::metric_primitive::MetricPrimitive; use crate::settings::CssEntry; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; #[derive(Debug, Clone, Serialize)] pub struct LogEntryDto { pub sequence: u64, pub timestamp_ms: i64, pub text: String, /// Derived param values (ticket 001's `ParamDeclaration`s) for this line, /// keyed by param name. Only params whose regex actually matched this /// line's text (and whose captured group parsed as the declared `kind`) /// are present — a non-matching param is omitted rather than sent as /// null, so the frontend can render a blank/dash for anything missing /// from the map. pub params: BTreeMap, } #[derive(Debug, Clone, Serialize)] pub struct ParamDeclarationDto { pub name: String, pub message_match_regex: String, pub capture_group: String, pub kind: MetricPrimitiveKind, } #[derive(Debug, Clone, Serialize)] pub struct MetricDeclarationDto { pub name: String, pub message_match_regex: String, pub params: Vec, } #[derive(Debug, Clone, Serialize)] pub struct TimelineBucketDto { pub start_ms: i64, pub count: u64, } #[derive(Debug, Clone, Serialize)] pub struct StatsDto { pub log_count: u64, pub first_sequence: Option, pub last_sequence: Option, pub first_timestamp_ms: Option, pub last_timestamp_ms: Option, } #[derive(Debug, Deserialize)] pub struct LogsParams { pub since_sequence: Option, pub start_ms: Option, pub end_ms: Option, pub limit: Option, } #[derive(Debug, Deserialize)] pub struct TimelineParams { pub start_ms: Option, pub end_ms: Option, pub bucket_ms: Option, } /// One entry from a metric's derived bucket timeseries: the label combination /// that bucket represents, plus one `(sequence, timestamp, value)` occurrence /// within it. #[derive(Debug, Clone, Serialize)] pub struct BucketValueDto { pub labels: BTreeMap, pub sequence: u64, pub timestamp_ms: i64, pub value: MetricPrimitive, } /// Query params for `GET /api/metrics/:name`. `labels` is a single /// comma-separated `key=value` list (e.g. `?labels=path=/a,duration=5`) /// rather than repeated `label.foo=bar` params, since axum's `Query` /// extractor needs a fixed/known set of field names — an arbitrary label set /// doesn't fit that without a custom deserializer. Every listed label must /// match (compared against each label value's `Display` form, see /// `MetricPrimitive`); unlisted labels on a bucket are ignored. #[derive(Debug, Deserialize)] pub struct BucketValuesParams { pub labels: Option, pub since_sequence: Option, pub start_ms: Option, pub end_ms: Option, pub limit: Option, } /// One chart-ready time series returned by `GET /api/query` (ticket 013's PromQL /// endpoint): a label combination plus its `(timestamp_ms, value)` points, sorted by /// timestamp. `labels` uses plain `String` values (not `MetricPrimitive`) since every /// value has already been through `MetricPrimitive`'s `Display` form — chart code /// doesn't need the typed value, just something to show in a legend/tooltip. /// /// `value` is always `f64` (see `crate::engine::coerce_to_f64` for how a bucket's /// `MetricPrimitive` is coerced into one), since a chart needs a plottable number /// regardless of the underlying metric primitive's kind. #[derive(Debug, Clone, Serialize, PartialEq)] pub struct PromqlSeriesDto { pub labels: BTreeMap, pub points: Vec<(i64, f64)>, } /// Query params for `GET /api/query`. `promql` is the query string (see /// `crate::promql`); `start_ms`/`end_ms` bound which points are included, mirroring /// `/api/logs`/`/api/metrics/:name`. #[derive(Debug, Deserialize)] pub struct PromqlParams { pub promql: String, pub start_ms: Option, pub end_ms: Option, } /// JSON error body returned by API handlers on a 4xx/5xx response, e.g. /// `{ "error": "invalid regex: ..." }`. #[derive(Debug, Serialize)] pub struct ErrorDto { pub error: String, } /// Request body for `POST /api/settings/css`: raw CSS content to import and /// store alongside the settings file. #[derive(Debug, Deserialize)] pub struct ImportCssRequest { pub filename: String, pub content: String, } /// Request body for `POST /api/settings/css/link`: a reference to an /// external CSS path/URL that ottyl doesn't copy or own. #[derive(Debug, Deserialize)] pub struct LinkCssRequest { pub path_or_url: String, } /// Response shape for a single CSS entry, used both by the list endpoint and /// by mutation endpoints that return the affected entry. `id` is the entry's /// real id for `Imported` entries, or a stable id derived from the /// linked path/URL for `Linked` entries (see `settings::css_entry_id`) — /// this lets the frontend key/reference both kinds of entries uniformly, /// even though only `Imported` entries are deletable/content-fetchable. #[derive(Debug, Clone, Serialize)] pub struct CssEntryDto { pub id: String, pub kind: CssEntryKind, pub filename: Option, pub path_or_url: Option, } #[derive(Debug, Clone, Copy, Serialize)] #[serde(rename_all = "lowercase")] pub enum CssEntryKind { Imported, Linked, } impl From<&CssEntry> for CssEntryDto { fn from(entry: &CssEntry) -> Self { match entry { CssEntry::Imported { id, filename } => CssEntryDto { id: id.clone(), kind: CssEntryKind::Imported, filename: Some(filename.clone()), path_or_url: None, }, CssEntry::Linked { path_or_url } => CssEntryDto { id: crate::settings::css_entry_id(entry), kind: CssEntryKind::Linked, filename: None, path_or_url: Some(path_or_url.clone()), }, } } }