1use std::fmt;
2use std::str::FromStr;
3
4use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
5
6use crate::config::Actor;
7use crate::error::Error;
8use crate::session::SessionId;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct RequestId(pub(crate) i64);
13
14impl fmt::Display for RequestId {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 write!(f, "r{}", self.0)
17 }
18}
19
20impl FromStr for RequestId {
21 type Err = Error;
22
23 fn from_str(text: &str) -> Result<Self, Self::Err> {
24 let not_found = || Error::RequestNotFound(text.to_owned());
25 let digits = text.strip_prefix('r').ok_or_else(not_found)?;
26 let row: i64 = digits.parse().map_err(|_| not_found())?;
27 Ok(Self(row))
28 }
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct LandingRequest {
36 pub id: RequestId,
38 pub session_id: SessionId,
40 pub requester: Actor,
42 pub state: RequestState,
44 pub approvals: Vec<Approval>,
46 pub created_at_ms: i64,
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum RequestState {
53 Open,
55 Approved,
57 Landed,
59 Parked,
61 Rejected,
63 Abandoned,
65}
66
67impl RequestState {
68 #[must_use]
70 pub fn as_str(self) -> &'static str {
71 match self {
72 Self::Open => "open",
73 Self::Approved => "approved",
74 Self::Landed => "landed",
75 Self::Parked => "parked",
76 Self::Rejected => "rejected",
77 Self::Abandoned => "abandoned",
78 }
79 }
80}
81
82impl fmt::Display for RequestState {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 f.write_str(self.as_str())
85 }
86}
87
88impl FromStr for RequestState {
89 type Err = Error;
90
91 fn from_str(text: &str) -> Result<Self, Self::Err> {
92 match text {
93 "open" => Ok(Self::Open),
94 "approved" => Ok(Self::Approved),
95 "landed" => Ok(Self::Landed),
96 "parked" => Ok(Self::Parked),
97 "rejected" => Ok(Self::Rejected),
98 "abandoned" => Ok(Self::Abandoned),
99 other => Err(Error::Engine(format!("unknown request state: {other}"))),
100 }
101 }
102}
103
104impl ToSql for RequestState {
105 fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
106 Ok(ToSqlOutput::from(self.as_str()))
107 }
108}
109
110impl FromSql for RequestState {
111 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
112 let text = value.as_str()?;
113 Self::from_str(text).map_err(|error| FromSqlError::Other(error.to_string().into()))
114 }
115}
116
117#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct Approval {
121 pub actor: Actor,
123 pub snapshot: String,
125 pub at_ms: i64,
127}
128
129#[derive(Debug, Clone, PartialEq, Eq)]
132pub struct Restore {
133 pub source: Option<String>,
135 pub head: String,
137}
138
139#[derive(Debug, Clone, PartialEq, Eq)]
142pub struct Landing {
143 pub source: Option<String>,
145 pub snapshot: String,
147}
148
149#[derive(Debug, Clone, PartialEq, Eq)]
153pub enum GateOutcome {
154 Landed {
156 landings: Vec<Landing>,
158 },
159 Pending {
161 request: LandingRequest,
163 required: u32,
165 },
166 Parked {
170 request: LandingRequest,
172 landings: Vec<Landing>,
174 parked: Vec<Option<String>>,
176 },
177}