OXYGENHUB
# 1e23d0105921 oxygenhub / crates / hub-api / src / lib.rs
6.0 KiB · Rust · last changed Milestone 0.3: proposals and review 1 month ago source blame history raw
1
//! Types of the Oxygenhub JSON API (hub spec §12.1). Everything here is
2
//! `serde` data with no behaviour, so clients in other languages can be
3
//! generated from it and the server can never drift from them.
4
//!
5
//! Shapes reuse `oxy --json`'s where one exists (hashes as lowercase hex,
6
//! times as milliseconds since the epoch), so tools written against the
7
//! command line work against the hub.
8
9
use serde::{Deserialize, Serialize};
10
11
/// A repository as the API lists it.
12
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
13
pub struct Repository {
14
    pub owner: String,
15
    pub name: String,
16
    pub description: String,
17
    pub default_bookmark: String,
18
    pub visibility: Visibility,
19
    /// Milliseconds since the epoch.
20
    pub created_ms: i64,
21
    pub updated_ms: i64,
22
    /// `https://…/{owner}/{name}`, what `oxy clone` takes.
23
    pub clone_url: String,
24
}
25
26
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
27
#[serde(rename_all = "lowercase")]
28
pub enum Visibility {
29
    Public,
30
    Internal,
31
    Private,
32
}
33
34
impl Visibility {
35
    pub fn as_str(self) -> &'static str {
36
        match self {
37
            Visibility::Public => "public",
38
            Visibility::Internal => "internal",
39
            Visibility::Private => "private",
40
        }
41
    }
42
43
    pub fn parse(s: &str) -> Option<Visibility> {
44
        Some(match s {
45
            "public" => Visibility::Public,
46
            "internal" => Visibility::Internal,
47
            "private" => Visibility::Private,
48
            _ => return None,
49
        })
50
    }
51
}
52
53
/// A bookmark: a name for a set of heads (spec §10.1).
54
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
55
pub struct Bookmark {
56
    pub name: String,
57
    pub heads: Vec<String>,
58
    /// Number of changes in the bookmark's state.
59
    pub changes: usize,
60
    pub default: bool,
61
}
62
63
/// A tag: a name for one state, with its attestation when signed.
64
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
65
pub struct Tag {
66
    pub name: String,
67
    pub heads: Vec<String>,
68
    pub message: String,
69
    pub signer: Option<String>,
70
}
71
72
/// One change in a history listing.
73
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
74
pub struct ChangeSummary {
75
    pub hash: String,
76
    pub message: String,
77
    pub author: Author,
78
    pub when_ms: u64,
79
    pub deps: Vec<String>,
80
    pub head: bool,
81
    pub signature: Signature,
82
}
83
84
/// Who wrote a change: the linked account when the signing key is linked,
85
/// else the key alone.
86
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
87
pub struct Author {
88
    pub name: String,
89
    pub email: String,
90
    pub key: String,
91
    /// The hub account this key is linked to, when any.
92
    pub account: Option<String>,
93
}
94
95
/// Signature status of a change as `oxy verify` reports it.
96
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
97
#[serde(tag = "status", rename_all = "lowercase")]
98
pub enum Signature {
99
    Unsigned,
100
    Valid { key: String, sealed: bool, revoked: bool },
101
    Invalid { reason: String },
102
}
103
104
/// An RFC 7807 problem response.
105
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
106
pub struct Problem {
107
    pub status: u16,
108
    pub title: String,
109
    pub detail: String,
110
}
111
112
#[cfg(test)]
113
mod tests {
114
    use super::*;
115
116
    #[test]
117
    fn visibility_round_trips() {
118
        for v in [Visibility::Public, Visibility::Internal, Visibility::Private] {
119
            assert_eq!(Visibility::parse(v.as_str()), Some(v));
120
        }
121
        assert_eq!(Visibility::parse("secret"), None);
122
    }
123
}
124
125
// ---------------------------------------------------------------- proposals
126
127
/// A proposal: a request to add changes to a target bookmark (hub spec §7).
128
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
129
pub struct Proposal {
130
    pub number: i64,
131
    pub title: String,
132
    pub body: String,
133
    pub author: String,
134
    pub state: ProposalState,
135
    pub target: String,
136
    pub source: ProposalSource,
137
    /// The changes the proposal adds, dependency order, newest last.
138
    pub changes: Vec<ChangeSummary>,
139
    /// Heads of the projected state (target plus the changes), when computed.
140
    pub projected_heads: Vec<String>,
141
    pub projected_state: Option<String>,
142
    pub conflicted: bool,
143
    pub approvals: Vec<Approval>,
144
    pub landable: bool,
145
    pub blockers: Vec<String>,
146
    pub created_ms: i64,
147
    pub updated_ms: i64,
148
    /// `https://hub/{owner}/{name}/proposal/{number}`.
149
    pub url: String,
150
    /// Where `oxy` fetches the changes from.
151
    pub repository_url: String,
152
    /// The fork's clone URL when the source is a fork.
153
    pub source_url: Option<String>,
154
}
155
156
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
157
#[serde(rename_all = "lowercase")]
158
pub enum ProposalState {
159
    Draft,
160
    Open,
161
    Landed,
162
    Closed,
163
}
164
165
impl ProposalState {
166
    pub fn as_str(self) -> &'static str {
167
        match self {
168
            ProposalState::Draft => "draft",
169
            ProposalState::Open => "open",
170
            ProposalState::Landed => "landed",
171
            ProposalState::Closed => "closed",
172
        }
173
    }
174
175
    pub fn parse(s: &str) -> Option<ProposalState> {
176
        Some(match s {
177
            "draft" => ProposalState::Draft,
178
            "open" => ProposalState::Open,
179
            "landed" => ProposalState::Landed,
180
            "closed" => ProposalState::Closed,
181
            _ => return None,
182
        })
183
    }
184
}
185
186
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
187
#[serde(tag = "kind", rename_all = "lowercase")]
188
pub enum ProposalSource {
189
    /// A bookmark of the same repository.
190
    Bookmark { bookmark: String },
191
    /// A bookmark of a fork.
192
    Fork { owner: String, name: String, bookmark: String },
193
    /// An explicit list of change hashes.
194
    Changes { hashes: Vec<String> },
195
}
196
197
/// A reviewer's approval, counted only when backed by a seal over every
198
/// change of the proposal signed with a key linked to the reviewer.
199
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
200
pub struct Approval {
201
    pub account: String,
202
    pub sealed: bool,
203
    pub key: Option<String>,
204
}