shared/domain/
locale.rs

1//! Locale types
2
3use std::collections::BTreeMap;
4
5use crate::api::endpoints::PathPart;
6use macros::make_path_parts;
7use serde::{Deserialize, Serialize};
8use uuid::Uuid;
9
10/// A bundle of [`Entry`]s
11#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
12#[serde(rename_all = "camelCase")]
13pub struct Bundle {
14    /// The bundle's id
15    pub id: Uuid,
16
17    /// the bundle's name
18    pub name: String,
19}
20
21/// What kind of item an [`Entry`] is.
22#[derive(Serialize, Deserialize, Clone, Debug)]
23#[serde(rename_all = "camelCase")]
24pub struct ItemKind {
25    /// The item kind's id
26    pub id: Uuid,
27
28    /// the item kind's name
29    pub name: String,
30}
31
32/// The status of a given [`Entry`]
33#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
34#[serde(rename_all = "camelCase")]
35#[cfg_attr(feature = "backend", derive(sqlx::Type))]
36#[repr(i16)]
37pub enum EntryStatus {
38    /// The entry has been approved.
39    Approved = 0,
40
41    /// The entry is in discussion.
42    Discuss = 1,
43
44    // todo: what does this even *mean*
45    /// The entry is on hold.
46    OnHold = 2,
47}
48
49// todo: an entry into the what?
50/// An entry into the ?
51#[derive(Serialize, Deserialize, Clone, Debug)]
52#[serde(rename_all = "camelCase")]
53pub struct Entry {
54    /// This entry's id
55    pub id: u32,
56
57    /// This entry's parent [`Bundle`]'s id
58    pub bundle_id: Uuid,
59
60    /// The section this entry belongs in
61    pub section: Option<String>,
62
63    /// This entry's [`ItemKind`]'s id.
64    pub item_kind_id: Option<Uuid>,
65
66    /// The English version of this entry.
67    pub english: Option<String>,
68
69    /// The hebrew version of this entry.
70    pub hebrew: Option<String>,
71
72    /// This entry's current status.
73    pub status: EntryStatus,
74
75    /// A reference url in zeplin
76    pub zeplin_reference: Option<String>,
77
78    /// This entry's comments
79    pub comments: Option<String>,
80
81    /// If the entry is in the app.
82    pub in_app: bool,
83
84    /// If the entry is in an element.
85    pub in_element: bool,
86
87    /// If the entry is in mock.
88    pub in_mock: bool,
89}
90
91make_path_parts!(CreateEntryPath => "/v1/locale/entry");
92
93/// Request for creating an entry.
94#[derive(Serialize, Deserialize, Clone, Debug)]
95#[serde(rename_all = "camelCase")]
96pub struct CreateEntryRequest {
97    /// This entry's parent [`Bundle`]'s id
98    pub bundle_id: Uuid,
99
100    /// The section this entry belongs in
101    pub section: Option<String>,
102
103    /// This entry's [`ItemKind`]'s id.
104    pub item_kind_id: Option<Uuid>,
105
106    /// The English version of this entry.
107    pub english: Option<String>,
108
109    /// The hebrew version of this entry.
110    pub hebrew: Option<String>,
111
112    /// This entry's current status.
113    pub status: EntryStatus,
114
115    /// A reference url in zeplin
116    pub zeplin_reference: Option<String>,
117
118    /// This entry's comments
119    pub comments: Option<String>,
120
121    /// If the entry is in the app.
122    pub in_app: bool,
123
124    /// If the entry is in an element.
125    pub in_element: bool,
126
127    /// If the entry is in mock.
128    pub in_mock: bool,
129}
130
131/// Response for successful creation of an entry.
132#[derive(Serialize, Deserialize, Clone, Debug)]
133#[serde(rename_all = "camelCase")]
134pub struct CreateEntryResponse {
135    /// The newly created [`Entry`]'s id.
136    pub id: u32,
137}
138
139/// Group by modifier for listing entries
140#[derive(Serialize, Deserialize, Clone, Debug)]
141#[serde(rename_all = "camelCase")]
142#[non_exhaustive]
143pub enum ListEntryGroupBy {
144    /// No grouping, just return a plain list
145    None,
146
147    /// Group by parent bundle
148    Bundle,
149}
150
151impl ListEntryGroupBy {
152    /// Returns `true` if `self` is [`None`](Self::None).
153    pub fn is_none(&self) -> bool {
154        matches!(self, Self::None)
155    }
156
157    /// Returns `true` if `self` is [`Bundle`](Self::Bundle).
158    pub fn is_bundle(&self) -> bool {
159        matches!(self, Self::Bundle)
160    }
161}
162
163impl Default for ListEntryGroupBy {
164    fn default() -> Self {
165        Self::None
166    }
167}
168
169make_path_parts!(ListEntryPath => "/v1/locale/entry");
170
171/// Query for listing [`entries`](Entry)
172#[derive(Serialize, Deserialize, Clone, Debug, Default)]
173#[serde(rename_all = "camelCase")]
174pub struct ListEntryQuery {
175    /// The [`Bundle`]s to filter to (empty means "all")
176    #[serde(skip_serializing_if = "Vec::is_empty")]
177    #[serde(default)]
178    #[serde(
179        serialize_with = "crate::domain::csv_encode_uuids",
180        deserialize_with = "crate::domain::from_csv"
181    )]
182    pub bundles: Vec<Uuid>,
183
184    /// Whether the response should be returned as
185    /// [`Bundles`](ListEntryResponse::Bundles) or [`List`](ListEntryResponse::List)
186    #[serde(skip_serializing_if = "ListEntryGroupBy::is_none")]
187    #[serde(default)]
188    pub group_by: ListEntryGroupBy,
189}
190
191/// Response for listing entries
192#[derive(Serialize, Deserialize, Clone, Debug)]
193#[serde(rename_all = "camelCase")]
194pub enum ListEntryResponse {
195    /// Entries grouped by [`Bundle`]
196    Bundles(BTreeMap<Uuid, Vec<Entry>>),
197
198    /// Ungrouped entries
199    List(Vec<Entry>),
200}
201
202// u32 is local id
203make_path_parts!(GetEntryPath => "/v1/locale/entry/{}" => u32);
204
205/// Response for getting a individual entry.
206#[derive(Serialize, Deserialize, Clone, Debug)]
207#[serde(rename_all = "camelCase")]
208pub struct GetEntryResponse {
209    /// The requested entry.
210    pub entry: Entry,
211}
212
213// u32 is local id
214make_path_parts!(UpdateEntryPath => "/v1/locale/entry/{}" => u32);
215
216/// Request for updating an [`Entry`]
217#[derive(Serialize, Deserialize, Clone, Debug)]
218#[serde(rename_all = "camelCase")]
219pub struct UpdateEntryRequest {
220    /// This entry's parent [`Bundle`]'s id
221    #[serde(skip_serializing_if = "Option::is_none")]
222    #[serde(default)]
223    pub bundle_id: Option<Uuid>,
224
225    /// The section this entry belongs in
226    #[serde(deserialize_with = "super::deserialize_optional_field")]
227    #[serde(skip_serializing_if = "Option::is_none")]
228    #[serde(default)]
229    pub section: Option<Option<String>>,
230
231    /// This entry's [`ItemKind`]'s id.
232    #[serde(skip_serializing_if = "Option::is_none")]
233    #[serde(default)]
234    pub item_kind_id: Option<Uuid>,
235
236    /// The English version of this entry.
237    #[serde(skip_serializing_if = "Option::is_none")]
238    #[serde(default)]
239    pub english: Option<String>,
240
241    /// The hebrew version of this entry.
242    #[serde(skip_serializing_if = "Option::is_none")]
243    #[serde(default)]
244    pub hebrew: Option<String>,
245
246    /// This entry's current status.
247    #[serde(skip_serializing_if = "Option::is_none")]
248    #[serde(default)]
249    pub status: Option<EntryStatus>,
250
251    /// A reference url in zeplin
252    #[serde(deserialize_with = "super::deserialize_optional_field")]
253    #[serde(skip_serializing_if = "Option::is_none")]
254    #[serde(default)]
255    pub zeplin_reference: Option<Option<String>>,
256
257    /// This entry's comments
258    #[serde(deserialize_with = "super::deserialize_optional_field")]
259    #[serde(skip_serializing_if = "Option::is_none")]
260    #[serde(default)]
261    pub comments: Option<Option<String>>,
262
263    /// If the entry is in the app.
264    #[serde(skip_serializing_if = "Option::is_none")]
265    #[serde(default)]
266    pub in_app: Option<bool>,
267
268    /// If the entry is in an element.
269    #[serde(skip_serializing_if = "Option::is_none")]
270    #[serde(default)]
271    pub in_element: Option<bool>,
272
273    /// If the entry is in mock.
274    #[serde(skip_serializing_if = "Option::is_none")]
275    #[serde(default)]
276    pub in_mock: Option<bool>,
277}
278
279// u32 is local id
280make_path_parts!(DeleteEntryPath => "/v1/locale/entry/{}" => u32);
281
282make_path_parts!(ListBundlePath => "/v1/locale/bundle");
283
284/// Response for listing bundles
285#[derive(Serialize, Deserialize, Clone, Debug)]
286#[serde(rename_all = "camelCase")]
287pub struct ListBundleResponse {
288    /// A list of bundles
289    pub bundles: Vec<Bundle>,
290}
291
292make_path_parts!(ListItemKindPath => "/v1/locale/item-kind");
293
294/// Response for listing item kinds
295#[derive(Serialize, Deserialize, Clone, Debug)]
296#[serde(rename_all = "camelCase")]
297pub struct ListItemKindResponse {
298    /// A list of item kinds
299    pub item_kinds: Vec<ItemKind>,
300}