Skip to main content

engenho_diff/
observe.rs

1//! The response half of the harness — a typed [`Observation`] captured
2//! from a [`DiffTarget`](crate::target::DiffTarget), plus the [`HttpMethod`]
3//! verb and the [`DiffError`] failure taxonomy.
4
5use serde_json::Value;
6use thiserror::Error;
7
8/// The HTTP verb of an [`Operation`](crate::op::Operation) — typed, so the
9/// harness never string-matches a method.
10#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub enum HttpMethod {
12    Get,
13    Post,
14    Put,
15    Patch,
16    Delete,
17}
18
19impl HttpMethod {
20    /// The `reqwest::Method` this verb maps to.
21    #[must_use]
22    pub fn as_reqwest(self) -> reqwest::Method {
23        match self {
24            Self::Get => reqwest::Method::GET,
25            Self::Post => reqwest::Method::POST,
26            Self::Put => reqwest::Method::PUT,
27            Self::Patch => reqwest::Method::PATCH,
28            Self::Delete => reqwest::Method::DELETE,
29        }
30    }
31
32    /// A stable label for reports + signatures.
33    #[must_use]
34    pub fn label(self) -> &'static str {
35        match self {
36            Self::Get => "GET",
37            Self::Post => "POST",
38            Self::Put => "PUT",
39            Self::Patch => "PATCH",
40            Self::Delete => "DELETE",
41        }
42    }
43}
44
45/// One observed response from a target: the wire status, the content-type,
46/// and the parsed body.
47#[derive(Clone, Debug)]
48pub struct Observation {
49    /// HTTP status code.
50    pub status: u16,
51    /// `Content-Type` header, if present.
52    pub content_type: Option<String>,
53    /// The parsed response body. `Value::Null` when the body was empty;
54    /// a non-JSON body is wrapped as `Value::String` (never silently
55    /// dropped).
56    pub json: Value,
57}
58
59impl Observation {
60    /// `true` iff the status is in the 2xx range.
61    #[must_use]
62    pub fn is_success(&self) -> bool {
63        (200..300).contains(&self.status)
64    }
65}
66
67/// Failure taxonomy for target execution. A transport failure against the
68/// k3s reference is what the runner turns into
69/// [`Verdict::ReferenceUnreachable`](crate::verdict::Verdict::ReferenceUnreachable)
70/// — a fail-loud signal, never a silent skip.
71#[derive(Debug, Error)]
72pub enum DiffError {
73    /// The HTTP request itself failed (connection refused, TLS, DNS).
74    #[error("transport error against {side}: {source}")]
75    Transport {
76        side: &'static str,
77        #[source]
78        source: reqwest::Error,
79    },
80    /// A kube-client / kubeconfig error (auth, parse).
81    #[error("kube error: {0}")]
82    Kube(String),
83    /// Booting the in-process engenho failed.
84    #[error("engenho boot error: {0}")]
85    Boot(String),
86}