1use crate::{
2 Error,
3 FmlResult,
4};
5use serde::Deserialize;
6use std::collections::HashMap;
7
8#[derive(Deserialize)]
10pub struct ApiResponse<T> {
11 pub error: Option<String>,
15
16 pub data: Option<T>,
20
21 #[serde(flatten)]
23 pub unknown: HashMap<String, serde_json::Value>,
24}
25
26impl<T> ApiResponse<T> {
27 pub fn is_error(&self) -> bool {
31 self.error.is_some()
32 }
33
34 pub fn is_success(&self) -> bool {
38 self.error.is_none() && self.data.is_some()
39 }
40
41 pub fn is_valid_response(&self) -> bool {
45 self.is_error() || self.is_success()
46 }
47}
48
49impl<T> From<ApiResponse<T>> for FmlResult<T> {
50 fn from(response: ApiResponse<T>) -> Self {
51 match (response.data, response.error) {
52 (Some(_data), Some(_e)) => Err(Error::InvalidApiResponse),
53 (Some(data), None) => Ok(data),
54 (None, Some(e)) => Err(Error::Api(e)),
55 (None, None) => Err(Error::InvalidApiResponse),
56 }
57 }
58}
59
60#[derive(Debug, Deserialize)]
62pub struct Article {
63 pub apikey: Option<String>,
64 pub area: Option<String>,
65 pub author: Option<String>,
66 pub bitly: Option<String>,
67 pub city: Option<String>,
68 pub content: String,
69 pub content_hidden: String,
70 pub country: Option<String>,
71 pub countrycode: Option<String>,
72 pub created: String,
73 pub flag: u32,
74 pub gender: Option<u8>,
75 pub id: u64,
76 pub images: Vec<ArticleImage>,
77 pub ip: Option<String>,
78 pub keywords: Vec<ArticleKeyword>,
79 pub layout: u32,
80 pub metrics: ArticleMetrics,
81 pub openview: u32,
82 pub origin: Option<String>,
83 pub paragraphs: Vec<serde_json::Value>,
84 pub published: String,
85 pub site: u32,
86 pub siteorig: Option<serde_json::Value>,
87 pub slug: String,
88 #[serde(rename = "socialTruncate")]
89 pub social_truncate: bool,
90 pub spicy: bool,
91 pub status: u32,
92 pub title: Option<String>,
93 #[serde(rename = "type")]
94 pub article_type: u32,
95 pub updated: String,
96 pub url: String,
97 pub user: u64,
98 pub usermetrics: ArticleUsermetrics,
99 pub videos: Vec<serde_json::Value>,
100 pub vote: u32,
101
102 #[serde(flatten)]
103 pub unknown: HashMap<String, serde_json::Value>,
104}
105
106#[derive(Debug, Deserialize)]
107pub struct ArticleImage {
108 pub copyright: Option<String>,
109 pub height: u32,
110 pub legend: Option<serde_json::Value>,
111 pub name: String,
112 pub url: String,
113 pub usage: u32,
114 pub width: u32,
115
116 #[serde(flatten)]
117 pub unknown: HashMap<String, serde_json::Value>,
118}
119
120#[derive(Debug, Deserialize)]
121pub struct ArticleKeyword {
122 pub label: String,
123 pub rub: bool,
124 pub uid: u32,
125
126 #[serde(flatten)]
127 pub unknown: HashMap<String, serde_json::Value>,
128}
129
130#[derive(Debug, Deserialize)]
131pub struct ArticleMetrics {
132 pub article: u64,
133 pub comment: u32,
134 pub favorite: u32,
135 pub mod_negative: u32,
136 pub mod_positive: u32,
137 pub reports: u32,
138 pub smiley_amusing: u32,
139 pub smiley_funny: u32,
140 pub smiley_hilarious: u32,
141 pub smiley_weird: u32,
142 pub votes_down: u32,
143 pub votes_up: u32,
144
145 #[serde(flatten)]
146 pub unknown: HashMap<String, serde_json::Value>,
147}
148
149#[derive(Debug, Deserialize)]
150pub struct ArticleUsermetrics {
151 pub favorite: bool,
152 pub smiley: Option<serde_json::Value>,
153 pub votes: Option<serde_json::Value>,
154 #[serde(flatten)]
155 pub unknown: HashMap<String, serde_json::Value>,
156}
157
158#[cfg(test)]
159mod test {
160 use super::*;
161
162 const DATA_1: &str = include_str!("../test_data/DATA_1.json");
163
164 #[test]
165 fn data_1() {
166 let _data_1: ApiResponse<Vec<Article>> =
167 serde_json::from_str(DATA_1).expect("failed to parse");
168 }
169}