1use serde::{Deserialize, Deserializer, Serialize};
2
3pub mod challenge;
4pub mod ctf;
5pub mod machine;
6pub mod season;
7pub mod sherlock;
8pub mod user;
9pub mod vpn;
10
11pub(crate) fn deserialize_string_or_int<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
13where
14 D: Deserializer<'de>,
15{
16 let v = Option::<serde_json::Value>::deserialize(deserializer)?;
17 Ok(v.map(|v| match v {
18 serde_json::Value::String(s) => s,
19 serde_json::Value::Number(n) => n.to_string(),
20 other => other.to_string(),
21 }))
22}
23
24pub(crate) fn deserialize_bool_or_null<'de, D>(deserializer: D) -> Result<bool, D::Error>
26where
27 D: Deserializer<'de>,
28{
29 Ok(Option::<bool>::deserialize(deserializer)?.unwrap_or(false))
30}
31
32#[derive(Debug, Deserialize)]
33pub struct Paginated<T> {
34 pub data: Vec<T>,
35 pub links: PaginationLinks,
36 pub meta: PaginationMeta,
37}
38
39#[derive(Debug, Deserialize)]
40pub struct PaginationLinks {
41 pub first: Option<String>,
42 pub last: Option<String>,
43 pub prev: Option<String>,
44 pub next: Option<String>,
45}
46
47#[derive(Debug, Deserialize)]
48pub struct PaginationMeta {
49 pub current_page: u32,
50 pub from: Option<u32>,
51 pub last_page: u32,
52 pub per_page: u32,
53 pub to: Option<u32>,
54 pub total: u32,
55}
56
57#[derive(Debug, Deserialize, Serialize)]
58pub struct ActionResponse {
59 pub message: String,
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn paginated_machines() {
68 let json = include_str!("../../tests/fixtures/v5-machines-list.json");
69 let result: Paginated<serde_json::Value> = serde_json::from_str(json).unwrap();
70 assert_eq!(result.data.len(), 15);
71 assert_eq!(result.meta.total, 545);
72 assert_eq!(result.meta.last_page, 37);
73 assert_eq!(result.meta.current_page, 1);
74 }
75
76 #[test]
77 fn paginated_challenges() {
78 let json = include_str!("../../tests/fixtures/challenges-list.json");
79 let result: Paginated<serde_json::Value> = serde_json::from_str(json).unwrap();
80 assert_eq!(result.data.len(), 15);
81 assert_eq!(result.meta.total, 839);
82 }
83
84 #[test]
85 fn paginated_sherlocks() {
86 let json = include_str!("../../tests/fixtures/sherlocks-list.json");
87 let result: Paginated<serde_json::Value> = serde_json::from_str(json).unwrap();
88 assert!(!result.data.is_empty());
89 }
90
91 #[test]
92 fn action_response_container_start() {
93 let json = include_str!("../../tests/fixtures/container-start.json");
94 let result: ActionResponse = serde_json::from_str(json).unwrap();
95 assert_eq!(result.message, "Instance Created!");
96 }
97
98 #[test]
99 fn action_response_flag_incorrect() {
100 let json = include_str!("../../tests/fixtures/challenge-own-incorrect.json");
101 let result: ActionResponse = serde_json::from_str(json).unwrap();
102 assert_eq!(result.message, "Incorrect flag");
103 }
104
105 #[test]
106 fn bool_or_null_handles_null() {
107 #[derive(Deserialize)]
108 struct T {
109 #[serde(default, deserialize_with = "deserialize_bool_or_null")]
110 flag: bool,
111 }
112 let null: T = serde_json::from_str(r#"{"flag": null}"#).unwrap();
113 assert!(!null.flag);
114 let t: T = serde_json::from_str(r#"{"flag": true}"#).unwrap();
115 assert!(t.flag);
116 let missing: T = serde_json::from_str(r#"{}"#).unwrap();
117 assert!(!missing.flag);
118
119 assert!(serde_json::from_str::<T>(r#"{"flag": 0}"#).is_err());
121 assert!(serde_json::from_str::<T>(r#"{"flag": 1}"#).is_err());
122 assert!(serde_json::from_str::<T>(r#"{"flag": "true"}"#).is_err());
123 }
124
125 #[test]
126 fn string_or_int_handles_both() {
127 #[derive(Deserialize)]
128 struct T {
129 #[serde(default, deserialize_with = "deserialize_string_or_int")]
130 val: Option<String>,
131 }
132 let s: T = serde_json::from_str(r#"{"val": "50"}"#).unwrap();
133 assert_eq!(s.val.as_deref(), Some("50"));
134 let i: T = serde_json::from_str(r#"{"val": 0}"#).unwrap();
135 assert_eq!(i.val.as_deref(), Some("0"));
136 let n: T = serde_json::from_str(r#"{"val": null}"#).unwrap();
137 assert!(n.val.is_none());
138 }
139
140 #[test]
141 fn string_or_int_handles_bool() {
142 #[derive(Deserialize)]
143 struct T {
144 #[serde(default, deserialize_with = "deserialize_string_or_int")]
145 val: Option<String>,
146 }
147 let t: T = serde_json::from_str(r#"{"val": true}"#).unwrap();
148 assert_eq!(t.val.as_deref(), Some("true"));
149 let f: T = serde_json::from_str(r#"{"val": false}"#).unwrap();
150 assert_eq!(f.val.as_deref(), Some("false"));
151 }
152
153 #[test]
154 fn string_or_int_handles_float() {
155 #[derive(Deserialize)]
156 struct T {
157 #[serde(default, deserialize_with = "deserialize_string_or_int")]
158 val: Option<String>,
159 }
160 let f: T = serde_json::from_str(r#"{"val": 4.5}"#).unwrap();
161 assert_eq!(f.val.as_deref(), Some("4.5"));
162 }
163
164 #[test]
165 fn string_or_int_handles_empty_string() {
166 #[derive(Deserialize)]
167 struct T {
168 #[serde(default, deserialize_with = "deserialize_string_or_int")]
169 val: Option<String>,
170 }
171 let e: T = serde_json::from_str(r#"{"val": ""}"#).unwrap();
172 assert_eq!(e.val.as_deref(), Some(""));
173 }
174}