Skip to main content

detritus_protocol/
schema.rs

1//! Per-tenant JSON Schema descriptors for crash and log payload validation.
2//!
3//! This module defines the public types consumed by `detritus-server` when
4//! loading and eventually applying per-project validation rules.  The actual
5//! JSON Schema compilation and validation lives in the server; the protocol
6//! crate only owns the wire-level taxonomy (`SchemaKind`) and the on-disk
7//! descriptor (`SchemaSpec`).
8//!
9//! # Error taxonomy
10//!
11//! [`SchemaError`] is the unified error type for
12//! all schema operations: file I/O, JSON parsing, validation failures, and
13//! look-ups for projects that were never registered.
14
15use std::path::PathBuf;
16
17use serde::{Deserialize, Serialize};
18
19/// The two payload shapes that may carry a per-tenant JSON Schema.
20///
21/// The `snake_case` wire names (`"crash_metadata"`, `"log_attributes"`) are
22/// used both in tokens TOML configuration and in any future API surface.
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
24#[serde(rename_all = "snake_case")]
25pub enum SchemaKind {
26    /// Validates the `metadata` field of a [`crate::CrashMetadata`] payload.
27    CrashMetadata,
28    /// Validates the resource / scope attributes of an OTLP log record.
29    LogAttributes,
30}
31
32/// One schema-file declaration as it appears in `tokens.toml`.
33///
34/// `path` is always resolved relative to the tokens config file's parent
35/// directory, never relative to the current working directory.
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct SchemaSpec {
38    /// The payload kind this schema governs.
39    pub kind: SchemaKind,
40    /// Path to the JSON Schema document on disk.
41    pub path: PathBuf,
42}
43
44/// Errors produced during schema loading or validation.
45#[derive(Debug, thiserror::Error)]
46#[non_exhaustive]
47pub enum SchemaError {
48    /// A schema file could not be read from disk.
49    #[error("schema I/O error reading `{path}`: {source}")]
50    Io {
51        /// Path that could not be read.
52        path: PathBuf,
53        /// Underlying I/O error.
54        #[source]
55        source: std::io::Error,
56    },
57
58    /// A schema file was read but is not valid JSON.
59    #[error("schema parse error in `{path}`: {source}")]
60    Parse {
61        /// Path of the offending file.
62        path: PathBuf,
63        /// JSON parse error.
64        #[source]
65        source: serde_json::Error,
66    },
67
68    /// A payload failed validation against the registered schema.
69    ///
70    /// This variant is produced by `validate`; it is never produced by `load`.
71    #[error("validation failed for {kind:?}: {errors:?}")]
72    Validation {
73        /// The kind of schema that rejected the payload.
74        kind: SchemaKind,
75        /// Human-readable description of each validation failure.
76        errors: Vec<String>,
77    },
78
79    /// A handler requested validation for a `(project, kind)` pair that was
80    /// never registered in the [`crate::schema`] registry.
81    #[error("no schema registered for project `{project}` / kind `{kind:?}`")]
82    UnknownSchema {
83        /// Project identifier that was not found.
84        project: String,
85        /// The schema kind that was requested.
86        kind: SchemaKind,
87    },
88}