1use crate::{
2 Call, Callsign, News, Node, OutgoingCall, OutgoingNews, Rubric, Statistics, Transmitter,
3 TransmitterGroup,
4};
5use reqwest::StatusCode;
6use serde::{Deserialize, Serialize};
7use url::Url;
8
9#[derive(Clone, Debug)]
10pub struct ClientConfig {
11 pub api_url: Url,
12}
13
14impl Default for ClientConfig {
15 fn default() -> Self {
16 Self {
17 api_url: Url::parse("https://hampager.de/api/").unwrap(),
18 }
19 }
20}
21
22#[derive(Clone, Debug)]
23pub struct Client {
24 client: reqwest::Client,
25 username: String,
26 password: String,
27 config: ClientConfig,
28}
29
30impl Client {
31 pub fn new(username: &str, password: &str) -> Self {
39 Self {
40 client: reqwest::Client::new(),
41 username: username.to_string(),
42 password: password.to_string(),
43 config: ClientConfig::default(),
44 }
45 }
46
47 async fn get<T: for<'de> Deserialize<'de>>(&self, path: &str) -> crate::Result<Option<T>> {
48 let result = self
49 .client
50 .get(self.config.api_url.join(path)?)
51 .basic_auth(&self.username, Some(&self.password))
52 .send()
53 .await?;
54
55 if result.status().is_success() {
56 Ok(Some(result.json().await?))
57 } else if result.status() == StatusCode::NOT_FOUND {
58 Ok(None)
59 } else {
60 Err(crate::Error::ApiError(result.status()))
61 }
62 }
63
64 async fn get_many<T: for<'de> Deserialize<'de>>(
65 &self,
66 path: &str,
67 ) -> crate::Result<Option<Vec<T>>> {
68 let result = self
69 .client
70 .get(self.config.api_url.join(path)?)
71 .basic_auth(&self.username, Some(&self.password))
72 .send()
73 .await?;
74
75 if result.status().is_success() {
76 Ok(Some(result.json().await?))
77 } else if result.status() == StatusCode::NOT_FOUND {
78 Ok(None)
79 } else {
80 Err(crate::Error::ApiError(result.status()))
81 }
82 }
83
84 async fn post<T: Serialize + ?Sized>(&self, path: &str, item: &T) -> crate::Result<()> {
85 let result = self
86 .client
87 .post(self.config.api_url.join(path)?)
88 .basic_auth(&self.username, Some(&self.password))
89 .json(item)
90 .send()
91 .await?;
92
93 if result.status().is_success() {
94 Ok(())
95 } else {
96 Err(crate::Error::ApiError(result.status()))
97 }
98 }
99
100 pub async fn get_statistics(&self) -> crate::Result<Option<Statistics>> {
101 self.get("stats").await
102 }
103
104 pub async fn get_calls_by(&self, owner: &str) -> crate::Result<Option<Vec<Call>>> {
105 self.get_many(&format!("calls?ownerName={}", owner)).await
106 }
107
108 pub async fn new_call(&self, call: &OutgoingCall) -> crate::Result<()> {
129 self.post("calls", call).await
130 }
131
132 pub async fn get_all_nodes(&self) -> crate::Result<Option<Vec<Node>>> {
133 self.get_many("nodes").await
134 }
135
136 pub async fn get_node(&self, name: &str) -> crate::Result<Option<Node>> {
137 self.get(&format!("nodes/{}", name)).await
138 }
139
140 pub async fn get_all_callsigns(&self) -> crate::Result<Option<Vec<Callsign>>> {
141 self.get_many("callsigns").await
142 }
143
144 pub async fn get_callsign(&self, name: &str) -> crate::Result<Option<Callsign>> {
145 self.get(&format!("callsigns/{}", name)).await
146 }
147
148 pub async fn get_all_transmitters(&self) -> crate::Result<Option<Vec<Transmitter>>> {
149 self.get_many("transmitters").await
150 }
151
152 pub async fn get_transmitter(&self, name: &str) -> crate::Result<Option<Transmitter>> {
153 self.get(&format!("transmitters/{}", name)).await
154 }
155
156 pub async fn get_all_transmitter_groups(&self) -> crate::Result<Option<Vec<TransmitterGroup>>> {
157 self.get_many("transmitterGroups").await
158 }
159
160 pub async fn get_transmitter_group(
161 &self,
162 name: &str,
163 ) -> crate::Result<Option<TransmitterGroup>> {
164 self.get(&format!("transmitterGroups/{}", name)).await
165 }
166
167 pub async fn get_all_rubrics(&self) -> crate::Result<Option<Vec<Rubric>>> {
168 self.get_many("rubrics").await
169 }
170
171 pub async fn get_rubric(&self, name: &str) -> crate::Result<Option<Rubric>> {
172 self.get(&format!("rubrics/{}", name)).await
173 }
174
175 pub async fn get_news(&self, name: &str) -> crate::Result<Option<Vec<News>>> {
176 match self
177 .get_many::<Option<News>>(&format!("news?rubricName={}", name))
178 .await?
179 {
180 Some(v) => Ok(Some(v.into_iter().flatten().collect())),
181 None => Ok(None),
182 }
183 }
184
185 pub async fn new_news(&self, news: &OutgoingNews) -> crate::Result<()> {
205 self.post("news", news).await
206 }
207}