Skip to main content

supercode_interchange/orchestration/
obligation.rs

1//! Delivery obligations (ยง2.6; Hermes `delivery_obligations`).
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7use crate::ontology::{Residue, SurfaceKey};
8
9/// Something attached to an outbound message.
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
11pub struct Attachment {
12    /// `image` | `file` | `video` | `audio`.
13    pub kind: String,
14    /// File name.
15    #[serde(default, skip_serializing_if = "Option::is_none")]
16    pub name: Option<String>,
17    /// MIME type.
18    #[serde(default, skip_serializing_if = "Option::is_none")]
19    pub mime: Option<String>,
20    /// Size in bytes.
21    #[serde(default, skip_serializing_if = "Option::is_none")]
22    pub size: Option<u64>,
23    /// A URL to fetch it from.
24    #[serde(default, skip_serializing_if = "Option::is_none")]
25    pub url: Option<String>,
26    /// A local path.
27    #[serde(default, skip_serializing_if = "Option::is_none")]
28    pub path: Option<String>,
29    /// Adapter data to re-fetch it.
30    #[serde(default, skip_serializing_if = "Option::is_none")]
31    pub r#ref: Option<Value>,
32}
33
34/// What is delivered.
35#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
36pub struct OutboundContent {
37    /// The text.
38    pub text: String,
39    /// Attachments.
40    #[serde(default, skip_serializing_if = "Option::is_none")]
41    pub attachments: Option<Vec<Attachment>>,
42    /// The message this replies to.
43    #[serde(default, skip_serializing_if = "Option::is_none")]
44    pub reply_to: Option<String>,
45    /// `text` | `markdown`.
46    #[serde(default, skip_serializing_if = "Option::is_none")]
47    pub format: Option<String>,
48}
49
50/// Delivery state; `sent` is Hermes's `delivered`.
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
52#[serde(rename_all = "snake_case")]
53pub enum ObligationState {
54    /// Not yet delivered.
55    Pending,
56    /// Delivered.
57    Sent,
58    /// Gave up.
59    Failed,
60    /// Dropped on purpose.
61    Dropped,
62}
63
64impl ObligationState {
65    /// Hermes's `delivery_obligations.state` word.
66    pub fn hermes_word(self) -> &'static str {
67        match self {
68            Self::Pending => "pending",
69            Self::Sent => "delivered",
70            Self::Failed => "failed",
71            Self::Dropped => "dropped",
72        }
73    }
74
75    /// The state for a Hermes word (`sent` is accepted as `delivered`).
76    pub fn from_hermes_word(word: &str) -> Option<Self> {
77        Some(match word {
78            "pending" => Self::Pending,
79            "delivered" | "sent" => Self::Sent,
80            "failed" => Self::Failed,
81            "dropped" => Self::Dropped,
82            _ => return None,
83        })
84    }
85}
86
87/// The platform's handle for what was sent.
88#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
89pub struct Posted {
90    /// The platform message id.
91    pub message_id: String,
92}
93
94/// Where an obligation came from.
95#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
96#[serde(tag = "kind", rename_all = "snake_case")]
97pub enum ObligationSource {
98    /// A conversation's turn.
99    Turn {
100        /// The surface, when recorded.
101        #[serde(default, skip_serializing_if = "Option::is_none")]
102        key: Option<SurfaceKey>,
103    },
104    /// A job fire.
105    Fire {
106        /// The fire.
107        fire_id: String,
108    },
109    /// A webhook.
110    Webhook {
111        /// The subscription name.
112        name: String,
113    },
114}
115
116/// One delivery obligation.
117#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
118pub struct Obligation {
119    /// The id.
120    pub id: String,
121    /// The surface it goes to.
122    pub target: SurfaceKey,
123    /// Hermes's `session_key` column, verbatim.
124    #[serde(default)]
125    pub session_key: Option<String>,
126    /// What is delivered.
127    pub content: OutboundContent,
128    /// State.
129    pub state: ObligationState,
130    /// Attempts so far.
131    #[serde(default)]
132    pub attempts: u64,
133    /// The last failure.
134    #[serde(default)]
135    pub last_error: Option<String>,
136    /// Creation instant (Hermes: epoch seconds as text).
137    pub created_at: String,
138    /// Last transition instant.
139    pub updated_at: String,
140    /// When it was sent.
141    #[serde(default)]
142    pub delivered_at: Option<String>,
143    /// The platform's handle for what was sent.
144    #[serde(default, skip_serializing_if = "Option::is_none")]
145    pub posted: Option<Posted>,
146    /// Where it came from.
147    pub source: ObligationSource,
148    /// Ledger columns the record does not model, verbatim.
149    #[serde(default)]
150    pub residue: Residue,
151}