tosho_mplus/proto/
common.rs

1//! A module containing some common models.
2//!
3//! If something is missing, please [open an issue](https://github.com/noaione/tosho-mango/issues/new/choose) or a [pull request](https://github.com/noaione/tosho-mango/compare).
4
5#![allow(clippy::derive_partial_eq_without_eq)]
6
7use std::str::FromStr;
8
9use tosho_macros::AutoGetter;
10
11use crate::helper::SubscriptionPlan;
12
13use super::enums::{ErrorAction, Language};
14
15/// A popup button action
16#[derive(Clone, PartialEq, ::prost::Message)]
17pub struct PopupButton {
18    /// The button action.
19    #[prost(string, optional, tag = "1")]
20    text: ::core::option::Option<::prost::alloc::string::String>,
21    // There is also a `action` but it's tied to system.
22    // So we don't need to implement it.
23}
24
25/// A default Popup response.
26#[derive(Clone, AutoGetter, PartialEq, ::prost::Message)]
27pub struct PopupMessage {
28    /// The message subject.
29    #[prost(string, optional, tag = "1")]
30    #[skip_field]
31    subject: ::core::option::Option<::prost::alloc::string::String>,
32    /// The message body.
33    #[prost(string, optional, tag = "2")]
34    #[skip_field]
35    body: ::core::option::Option<::prost::alloc::string::String>,
36    /// The OK button action.
37    #[prost(message, optional, tag = "3")]
38    ok_button: ::core::option::Option<PopupButton>,
39    /// The neutral button action.
40    #[prost(message, optional, tag = "4")]
41    neutral_button: ::core::option::Option<PopupButton>,
42    /// The cancel button action.
43    #[prost(message, optional, tag = "5")]
44    cancel_button: ::core::option::Option<PopupButton>,
45    /// The language of the message.
46    #[prost(enumeration = "Language", tag = "6")]
47    #[skip_field]
48    language: i32,
49}
50
51impl PopupMessage {
52    /// Format the popup message into a string.
53    pub fn as_string(&self) -> String {
54        let mut message = String::new();
55        if let Some(subject) = &self.subject {
56            message.push_str(subject);
57            message.push_str(": ");
58        }
59        if let Some(body) = &self.body {
60            message.push_str(body);
61        }
62        message
63    }
64}
65
66/// An error response.
67#[derive(Clone, AutoGetter, PartialEq, ::prost::Message)]
68pub struct ErrorResponse {
69    /// The error action or error code of the request.
70    #[prost(enumeration = "ErrorAction", tag = "1")]
71    #[skip_field]
72    action: i32,
73    /// English popup message
74    #[prost(message, optional, tag = "2")]
75    english_popup: ::core::option::Option<PopupMessage>,
76    /// Spanish popup message
77    #[prost(message, optional, tag = "3")]
78    spanish_popup: ::core::option::Option<PopupMessage>,
79    /// Debug message
80    #[prost(string, optional, tag = "4")]
81    #[skip_field]
82    debug_message: ::core::option::Option<::prost::alloc::string::String>,
83    /// Array of other popup messages
84    #[prost(message, repeated, tag = "5")]
85    other_popups: ::prost::alloc::vec::Vec<PopupMessage>,
86}
87
88impl ErrorResponse {
89    /// Format the error response into a string.
90    pub fn as_string(&self) -> String {
91        let popup_message = self.english_popup.as_ref().map(|popup| popup.as_string());
92        match self.action() {
93            ErrorAction::Default => {
94                let mut message = String::new();
95                message.push_str("An error occurred");
96                if let Some(popup_message) = popup_message {
97                    message.push_str(&format!(": {popup_message}"));
98                }
99                if let Some(debug_message) = &self.debug_message {
100                    message.push_str(&format!("\nDebug: {debug_message}"));
101                }
102                message
103            }
104            ErrorAction::Unauthorized => {
105                let mut message = String::new();
106                message.push_str("You are not authorized to access this resource");
107                if let Some(debug_message) = &self.debug_message {
108                    message.push_str(&format!("\nDebug: {debug_message}"));
109                }
110                message
111            }
112            ErrorAction::Maintenance => {
113                let mut message = String::new();
114                message.push_str("Server is under maintenance");
115                if let Some(debug_message) = &self.debug_message {
116                    message.push_str(&format!("\nDebug: {debug_message}"));
117                }
118                message
119            }
120            ErrorAction::GeoIPBlocked => {
121                let mut message = String::new();
122                message.push_str("Your request is blocked by GeoIP");
123                if let Some(debug_message) = &self.debug_message {
124                    message.push_str(&format!("\nDebug: {debug_message}"));
125                }
126                message
127            }
128            ErrorAction::Unrecognized => {
129                let mut message = String::new();
130                message.push_str("An unknown error occurred");
131                if let Some(debug_message) = &self.debug_message {
132                    message.push_str(&format!("\nDebug: {debug_message}"));
133                }
134                message
135            }
136        }
137    }
138}
139
140/// The banner data.
141#[derive(Clone, AutoGetter, PartialEq, ::prost::Message)]
142pub struct Banner {
143    /// The banner image.
144    #[prost(string, tag = "1")]
145    image: ::prost::alloc::string::String,
146    /// The associated ID.
147    #[prost(uint64, optional, tag = "3")]
148    #[skip_field]
149    id: Option<u64>,
150    /// The banner width.
151    #[prost(uint32, optional, tag = "4")]
152    #[skip_field]
153    width: Option<u32>,
154    /// The banner height.
155    #[prost(uint32, optional, tag = "5")]
156    #[skip_field]
157    height: Option<u32>,
158}
159
160/// A tag information
161#[derive(Clone, AutoGetter, PartialEq, ::prost::Message)]
162pub struct Tag {
163    /// Tag name
164    #[prost(string, tag = "1")]
165    name: ::prost::alloc::string::String,
166    /// Tag slug
167    #[prost(string, tag = "2")]
168    slug: ::prost::alloc::string::String,
169}
170
171/// A label information
172#[derive(Clone, AutoGetter, PartialEq, ::prost::Message)]
173pub struct Label {
174    /// Label ID
175    #[prost(uint64, tag = "1")]
176    id: u64,
177    /// Label description
178    #[prost(string, tag = "2")]
179    description: ::prost::alloc::string::String,
180}
181
182/// A publisher news information
183#[derive(Clone, AutoGetter, PartialEq, ::prost::Message)]
184pub struct PublisherNews {
185    /// Publisher news ID
186    #[prost(uint64, tag = "1")]
187    news_id: u64,
188    /// Publisher ID
189    #[prost(uint64, tag = "2")]
190    publisher_id: u64,
191    /// Publisher name
192    #[prost(string, tag = "3")]
193    name: ::prost::alloc::string::String,
194    /// Publisher news title
195    #[prost(string, tag = "4")]
196    title: ::prost::alloc::string::String,
197    /// Publisher news content
198    #[prost(string, tag = "5")]
199    content: ::prost::alloc::string::String,
200    /// Time of publication in UNIX timestamp
201    #[prost(uint64, tag = "6")]
202    published_at: u64,
203}
204
205/// A publisher information
206#[derive(Clone, AutoGetter, PartialEq, ::prost::Message)]
207pub struct PublisherItem {
208    /// The publisher banner info
209    #[prost(message, optional, tag = "1")]
210    banner: ::core::option::Option<Banner>,
211    /// The publisher news
212    #[prost(message, optional, tag = "2")]
213    news: ::core::option::Option<PublisherNews>,
214}
215
216/// Available languages in the source.
217#[derive(Clone, AutoGetter, PartialEq, Copy, ::prost::Message)]
218pub struct AvailableLanguages {
219    /// The language
220    #[prost(enumeration = "Language", tag = "1")]
221    #[skip_field]
222    language: i32,
223    /// Total count of titles available in the language
224    #[prost(uint64, tag = "2")]
225    titles_count: u64,
226}
227
228/// A information about the current languages
229#[derive(Clone, AutoGetter, PartialEq, ::prost::Message)]
230pub struct Languages {
231    /// The current UI language
232    #[prost(enumeration = "Language", tag = "1")]
233    #[skip_field]
234    language: i32,
235    /// The content language
236    #[prost(enumeration = "Language", optional, tag = "2")]
237    #[skip_field]
238    content_language: ::core::option::Option<i32>,
239    /// The secondary content language
240    ///
241    /// This will take priority over the first content language.
242    #[prost(enumeration = "Language", optional, tag = "3")]
243    #[skip_field]
244    content_language_secondary: ::core::option::Option<i32>,
245    /// The tertiary content language
246    ///
247    /// This will take priority over the first and second content language.
248    #[prost(enumeration = "Language", optional, tag = "4")]
249    #[skip_field]
250    content_language_tertiary: ::core::option::Option<i32>,
251    /// The available languages
252    #[prost(message, repeated, tag = "5")]
253    availables: ::prost::alloc::vec::Vec<AvailableLanguages>,
254}
255
256impl Languages {
257    /// Get the current active content language.
258    pub fn content_languages(&self) -> Language {
259        match (
260            self.content_language_tertiary,
261            self.content_language_secondary,
262            self.content_language,
263        ) {
264            (Some(_), _, _) => self.content_language_tertiary(),
265            (_, Some(_), _) => self.content_language_secondary(),
266            (_, _, Some(_)) => self.content_language(),
267            _ => Language::English,
268        }
269    }
270}
271
272/// A subscription offer for Android device
273#[derive(Clone, AutoGetter, PartialEq, ::prost::Message)]
274pub struct SubscriptionOfferAndroid {
275    /// The offer tag
276    #[prost(string, tag = "1")]
277    tag: ::prost::alloc::string::String,
278}
279
280/// A subscription offer for Apple device
281#[derive(Clone, AutoGetter, PartialEq, ::prost::Message)]
282pub struct SubscriptionOfferApple {
283    /// The offer type
284    #[prost(enumeration = "super::PlanOfferType", tag = "1")]
285    #[skip_field]
286    kind: i32,
287    /// The signature info
288    #[prost(string, tag = "2")]
289    signature: ::prost::alloc::string::String,
290    /// The apple key info
291    #[prost(string, tag = "3")]
292    key: ::prost::alloc::string::String,
293    /// The nonce info
294    #[prost(string, tag = "4")]
295    nonce: ::prost::alloc::string::String,
296    /// The timestamp info
297    #[prost(string, tag = "5")]
298    timestamp: ::prost::alloc::string::String,
299    /// The identifier info
300    #[prost(string, tag = "6")]
301    identifier: ::prost::alloc::string::String,
302}
303
304/// A plan information
305#[derive(Clone, AutoGetter, PartialEq, ::prost::Message)]
306pub struct Plan {
307    /// The plan name/ID
308    #[prost(string, tag = "1")]
309    #[skip_field]
310    name: ::prost::alloc::string::String,
311    /// The plan description
312    #[prost(string, tag = "2")]
313    description: ::prost::alloc::string::String,
314    /// The plan product ID
315    #[prost(string, tag = "3")]
316    product_id: ::prost::alloc::string::String,
317    /// The subscription offer for apple devices
318    #[prost(message, optional, tag = "4")]
319    apple_offer: ::core::option::Option<SubscriptionOfferApple>,
320    /// The subscription offer for android devices
321    #[prost(message, repeated, tag = "5")]
322    android_offer: ::prost::alloc::vec::Vec<SubscriptionOfferAndroid>,
323}
324
325impl Plan {
326    /// Get the actual subscriptions plan type
327    ///
328    /// This will return the actual [`SubscriptionPlan`] type
329    /// and fallback to [`SubscriptionPlan::Basic`] if the plan is not recognized.
330    pub fn name(&self) -> SubscriptionPlan {
331        match SubscriptionPlan::from_str(&self.name) {
332            Ok(plan) => plan,
333            Err(_) => SubscriptionPlan::Basic,
334        }
335    }
336}