Skip to main content

dre_core/
run_results.rs

1//! `run_results.json`: what one run of one Binding did. A public, versioned contract
2//! (`docs/run-results.schema.json` is generated from these types): adding an optional field
3//! keeps [`SCHEMA_VERSION`]; removing, renaming or re-typing one, or changing what it means,
4//! bumps it (a minor DRE release before 1.0, a major one after).
5
6use std::collections::BTreeMap;
7use std::path::PathBuf;
8
9use schemars::JsonSchema;
10use serde::Serialize;
11
12use crate::codes::{Code, ErrorCode, Kind};
13use crate::settings::Settings;
14
15type JsonMap = serde_json::Map<String, serde_json::Value>;
16
17/// The format's version.
18pub const SCHEMA_VERSION: &str = "dre/run-results/v1";
19
20/// `<target path>/run/<report>/<set or default>/runs/<run id>/run_results.json`: what one run of one Binding did. Written by `dre run` for each Binding it runs. See docs/manifest.md.
21#[derive(Debug, Clone, Serialize, JsonSchema)]
22#[schemars(title = "DRE run results")]
23pub struct RunResults {
24    /// The format's version: `dre/run-results/v1`.
25    #[schemars(schema_with = "schema_version")]
26    pub schema_version: &'static str,
27    /// This run's id (`20261009T060000Z-k3f9`: its UTC start and four random characters), the name of its folder under `runs/`.
28    pub run_id: String,
29    pub report: String,
30    /// Null for a report without Sets.
31    pub set: Option<String>,
32    /// The Binding's folder name: the Set's, or `default`.
33    pub binding: String,
34    /// False for a bare .sql under reports/ (an unmanaged report).
35    pub managed: bool,
36    /// The inherited connection (Set, report, folder `+profile`, `default_profile`), rendered.
37    pub profile: Option<String>,
38    /// Every connection the Binding's queries ran on.
39    pub connections: Vec<String>,
40    /// The run's target (environment): `--target`, else `DRE_TARGET`, else `dev`.
41    pub target: String,
42    /// The schedule it ran under (`--schedule`).
43    pub schedule: Option<String>,
44    /// The vars that schedule layered in.
45    pub schedule_vars: Option<JsonMap>,
46    /// Every var the Binding rendered with: its own, the schedule's, then `--var`.
47    pub vars: JsonMap,
48    /// `run.date`, `YYYY-MM-DD`.
49    pub run_date: String,
50    /// The instant it was scheduled for (`DRE_RUN_AT`), RFC 3339.
51    pub scheduled_at: Option<String>,
52    /// The run's timezone (IANA name).
53    pub timezone: String,
54    /// What the command was asked to do.
55    pub params: Params,
56    pub status: Status,
57    /// Why it failed.
58    pub error: Option<String>,
59    /// `error`'s code (see the error codes reference).
60    pub error_code: Option<ErrorCode>,
61    /// `error_code`'s kind.
62    pub error_kind: Option<Kind>,
63    /// Whether it was a `--preview`: never delivered.
64    pub preview: bool,
65    /// `--preview`'s row limit per query.
66    pub row_limit: Option<u64>,
67    /// When the Binding started, RFC 3339.
68    pub started_at: String,
69    pub duration_ms: u64,
70    /// Each result set its queries produced, in order.
71    pub result_sets: Vec<ResultSet>,
72    /// Every file every output wrote.
73    pub outputs: Vec<OutputFile>,
74    /// One entry per output, in declared order.
75    pub output_results: Vec<OutputResult>,
76    /// Why outputs weren't delivered, when that's by design (no destination, `deliver: false`, a preview).
77    pub delivery: Option<String>,
78    /// Every destination, in delivery order: file outputs, then messages.
79    pub deliveries: Vec<Delivery>,
80    /// How the result sets' columns changed since the last run, if they did.
81    pub schema_drift: Vec<String>,
82    /// The resolved target path.
83    pub target_path: PathBuf,
84    /// Every setting and where its value came from.
85    #[schemars(schema_with = "settings")]
86    pub settings: Settings,
87    /// The SHA-256 of the manifest.json bytes this run wrote.
88    pub manifest_checksum: Option<String>,
89}
90
91/// How a Binding ended.
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, JsonSchema)]
93#[serde(rename_all = "snake_case")]
94pub enum Status {
95    Success,
96    Error,
97    /// `--dry-run`: rendered, nothing executed.
98    DryRun,
99    /// `dre validate --live`: checked on the database, nothing executed.
100    Checked,
101    /// Stopped by Ctrl-C or a termination signal while it ran; nothing was delivered after.
102    Cancelled,
103    /// Stopped because the run took longer than its timeout; nothing was delivered after.
104    TimedOut,
105}
106
107/// The command's parameters.
108#[derive(Debug, Clone, Serialize, JsonSchema)]
109pub struct Params {
110    pub selector: Option<String>,
111    pub set: Option<String>,
112    pub schedule: Option<String>,
113    /// `--target` as given.
114    pub target: Option<String>,
115    /// `--profile`.
116    pub profile: Option<String>,
117    /// `--var`.
118    pub vars: BTreeMap<String, String>,
119    pub run_date: String,
120    pub scheduled_at: Option<String>,
121    /// `--timezone` or `DRE_TIMEZONE`.
122    pub timezone: Option<String>,
123    pub output_name: Option<String>,
124    pub output_path: Option<String>,
125    pub dry_run: bool,
126    pub preview: Option<u64>,
127    pub accept_schema_change: bool,
128}
129
130/// One result set.
131#[derive(Debug, Clone, Serialize, JsonSchema)]
132pub struct ResultSet {
133    /// Its name: the tab name.
134    pub name: String,
135    pub query: String,
136    /// The connection it came from.
137    pub connection: String,
138    pub rows: u64,
139    pub columns: Vec<String>,
140}
141
142/// One file an output wrote.
143#[derive(Debug, Clone, Serialize, JsonSchema)]
144pub struct OutputFile {
145    /// Relative to the project root, or to the target path when that's outside the project.
146    pub path: PathBuf,
147    pub size: u64,
148    /// Where it was delivered, when it was.
149    pub delivered_to: Option<String>,
150    /// The output's `name:`.
151    pub output: Option<String>,
152}
153
154/// How one output ended.
155#[derive(Debug, Clone, Serialize, JsonSchema)]
156pub struct OutputResult {
157    pub name: Option<String>,
158    pub format: String,
159    /// The queries it formatted.
160    pub queries: Vec<String>,
161    #[schemars(schema_with = "output_status")]
162    pub status: &'static str,
163    pub error: Option<String>,
164    pub files: Vec<OutputFileRef>,
165    pub delivery: Option<String>,
166    pub deliveries: Vec<Delivery>,
167    /// Its `when:`, evaluated.
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub when: Option<bool>,
170    /// A message output's rendered title and text, as sent.
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub message: Option<Message>,
173}
174
175/// One file of an output.
176#[derive(Debug, Clone, Serialize, JsonSchema)]
177pub struct OutputFileRef {
178    pub path: PathBuf,
179    pub size: u64,
180    pub delivered_to: Option<String>,
181}
182
183/// A message output's rendered title and text.
184#[derive(Debug, Clone, Serialize, JsonSchema)]
185pub struct Message {
186    pub title: String,
187    pub text: String,
188}
189
190/// One delivery to one destination.
191#[derive(Debug, Clone, Serialize, JsonSchema)]
192pub struct Delivery {
193    /// The destination profile.
194    pub profile: String,
195    /// Its plugin type; null for an entry that delivers nowhere.
196    #[serde(rename = "type")]
197    pub kind: Option<String>,
198    /// The profile's entry for the run.
199    pub target: String,
200    #[schemars(schema_with = "delivery_status")]
201    pub status: &'static str,
202    /// Where it went.
203    #[serde(skip_serializing_if = "Option::is_none")]
204    pub location: Option<String>,
205    /// How many tries it took, when the plugin had to try again after a temporary error.
206    #[serde(skip_serializing_if = "Option::is_none")]
207    pub attempts: Option<u32>,
208    #[serde(skip_serializing_if = "Option::is_none")]
209    pub error: Option<String>,
210    #[serde(skip_serializing_if = "Option::is_none")]
211    pub error_code: Option<ErrorCode>,
212}
213
214fn schema_version(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
215    schemars::json_schema!({"const": SCHEMA_VERSION})
216}
217
218fn output_status(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
219    schemars::json_schema!({"enum": ["delivered", "kept", "skipped", "failed"]})
220}
221
222fn delivery_status(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
223    schemars::json_schema!({"enum": ["delivered", "not_delivered", "failed"]})
224}
225
226fn settings(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
227    schemars::json_schema!({
228        "type": "array",
229        "items": {
230            "type": "object",
231            "properties": {
232                "name": {"type": "string"},
233                "value": {"type": ["string", "null"]},
234                "source": {"type": "string", "description": "A flag (`--target`), an environment variable (`DRE_TARGET`), a dre_project.yml key, how it was found, or `default`."}
235            },
236            "required": ["name", "value", "source"]
237        },
238        "x-dre-null": true
239    })
240}
241
242impl JsonSchema for Code {
243    fn schema_name() -> std::borrow::Cow<'static, str> {
244        "Code".into()
245    }
246    fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
247        schemars::json_schema!({"type": "string", "description": "A code from the error codes reference, e.g. `query-failed`."})
248    }
249    fn inline_schema() -> bool {
250        true
251    }
252}
253
254impl JsonSchema for ErrorCode {
255    fn schema_name() -> std::borrow::Cow<'static, str> {
256        "ErrorCode".into()
257    }
258    fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
259        schemars::json_schema!({"type": "string", "description": "A code from the error codes reference, e.g. `query-failed`, or a plugin's own, namespaced by the plugin, e.g. `sftp/host-key-mismatch`."})
260    }
261    fn inline_schema() -> bool {
262        true
263    }
264}
265
266impl JsonSchema for Kind {
267    fn schema_name() -> std::borrow::Cow<'static, str> {
268        "Kind".into()
269    }
270    fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
271        schemars::json_schema!({"enum": ["usage", "config", "plugin", "refused", "connection", "auth", "query", "delivery", "internal", "cancelled", "timed_out"]})
272    }
273    fn inline_schema() -> bool {
274        true
275    }
276}