1use std::fmt::Debug;
74
75use rusqlite::{types::FromSql, Connection};
76
77mod error;
78mod node;
79mod util;
80
81pub use error::*;
82pub use node::{
83 air::*, bus::*, located::*, rail::*, sea::*, spawn_warp::*, town::*, AnyNode, Node,
84};
85pub use util::ID;
86
87use crate::util::ConnectionExt;
88
89pub const URL: &str = "https://raw.githubusercontent.com/MRT-Map/gatelogue/refs/heads/dist/data.db";
90pub const URL_NO_SOURCES: &str =
91 "https://raw.githubusercontent.com/MRT-Map/gatelogue/refs/heads/dist/data-ns.db";
92
93#[macro_export]
94macro_rules! getter {
95 (reqwest) => {
96 async |url: &'static str| -> Result<Vec<u8>, reqwest::Error> {
97 Ok(reqwest::get(url).await?.bytes().await?.to_vec())
98 }
99 };
100 (reqwest_blocking) => {
101 |url: &'static str| -> Result<Vec<u8>, reqwest::Error> {
102 Ok(reqwest::blocking::get(url)?.bytes()?.to_vec())
103 }
104 };
105 (surf) => {
106 async |url: &'static str| -> Result<Vec<u8>, surf::Error> {
107 surf::get(url).recv_bytes().await
108 }
109 };
110 (ureq) => {
111 |url: &'static str| -> Result<Vec<u8>, ureq::Error> {
112 ureq::get(url).call()?.into_body().read_to_vec()
113 }
114 };
115 (isahc) => {
116 |url: &'static str| -> Result<Vec<u8>, isahc::Error> {
117 Ok(isahc::ReadResponseExt::bytes(&mut isahc::get(url)?)?)
118 }
119 };
120 (isahc_async) => {
121 async |url: &'static str| -> Result<Vec<u8>, isahc::Error> {
122 Ok(isahc::AsyncReadResponseExt::bytes(&mut isahc::get_async(url).await?).await?)
123 }
124 };
125 (attohttpc) => {
126 |url: &'static str| -> Result<Vec<u8>, attohttpc::Error> {
127 attohttpc::get(url).send()?.bytes()
128 }
129 };
130 (minreq) => {
131 |url: &'static str| -> Result<Vec<u8>, minreq::Error> {
132 Ok(minreq::get(url).send()?.into_bytes())
133 }
134 };
135 (wreq) => {
136 async |url: &'static str| -> Result<Vec<u8>, wreq::Error> {
137 Ok(wreq::get(url).send().await?.bytes().await?.to_vec())
138 }
139 };
140 (ehttp) => {
141 |url: &'static str| -> Result<Vec<u8>, ehttp::Error> {
142 Ok(ehttp::fetch_blocking(&ehttp::Request::get(url))?.bytes)
143 }
144 };
145 (ehttp_async) => {
146 async |url: &'static str| -> Result<Vec<u8>, ehttp::Error> {
147 Ok(ehttp::fetch_async(ehttp::Request::get(url)).await?.bytes)
148 }
149 };
150}
151
152pub struct GD(pub Connection);
153
154impl GD {
155 fn from_bytes(bytes: &[u8]) -> Result<Self> {
156 let mut conn = Connection::open_in_memory()?;
157 conn.deserialize_read_exact("main", bytes, bytes.len(), true)?;
158 Ok(Self(conn))
159 }
160 pub async fn get_async_with_sources<
161 F: AsyncFnOnce(&'static str) -> Result<B, E>,
162 B: AsRef<[u8]>,
163 E: Debug + Send + Sync + 'static,
164 >(
165 getter: F,
166 ) -> Result<Self> {
167 Self::from_bytes(
168 getter(URL)
169 .await
170 .map_err(|e| Error::HTTPGetError(Box::new(e)))?
171 .as_ref(),
172 )
173 }
174 pub async fn get_async_no_sources<
175 F: AsyncFnOnce(&'static str) -> Result<B, E>,
176 B: AsRef<[u8]>,
177 E: Debug + Send + Sync + 'static,
178 >(
179 getter: F,
180 ) -> Result<Self> {
181 Self::from_bytes(
182 getter(URL_NO_SOURCES)
183 .await
184 .map_err(|e| Error::HTTPGetError(Box::new(e)))?
185 .as_ref(),
186 )
187 }
188 pub fn get_with_sources<
189 F: FnOnce(&'static str) -> Result<B, E>,
190 B: AsRef<[u8]>,
191 E: Debug + Send + Sync + 'static,
192 >(
193 getter: F,
194 ) -> Result<Self> {
195 Self::from_bytes(
196 getter(URL)
197 .map_err(|e| Error::HTTPGetError(Box::new(e)))?
198 .as_ref(),
199 )
200 }
201 pub fn get_no_sources<
202 F: FnOnce(&'static str) -> Result<B, E>,
203 B: AsRef<[u8]>,
204 E: Debug + Send + Sync + 'static,
205 >(
206 getter: F,
207 ) -> Result<Self> {
208 Self::from_bytes(
209 getter(URL_NO_SOURCES)
210 .map_err(|e| Error::HTTPGetError(Box::new(e)))?
211 .as_ref(),
212 )
213 }
214
215 pub fn timestamp(&self) -> Result<String> {
216 self.0
217 .query_one("SELECT timestamp FROM Metadata", (), |a| a.get(0))
218 .map_err(Into::into)
219 }
220 pub fn version(&self) -> Result<u32> {
221 self.0
222 .query_one("SELECT version FROM Metadata", (), |a| a.get(0))
223 .map_err(Into::into)
224 }
225 pub fn has_sources(&self) -> Result<bool> {
226 self.0
227 .query_one("SELECT has_sources FROM Metadata", (), |a| a.get(0))
228 .map_err(Into::into)
229 }
230
231 pub fn nodes(&self) -> Result<Vec<AnyNode>> {
232 self.0
233 .query_and_then_get_vec("SELECT i FROM Node", (), |a| {
234 AnyNode::from_id(self, a.get(0)?).map(|a| a.unwrap())
235 })
236 }
237 pub fn located_nodes(&self) -> Result<Vec<AnyLocatedNode>> {
238 self.0
239 .query_and_then_get_vec("SELECT i FROM LocatedNode", (), |a| {
240 AnyLocatedNode::from_id(self, a.get(0)?).map(|a| a.unwrap())
241 })
242 }
243 pub fn nodes_of_type<T: Node + From<ID> + FromSql>(&self) -> Result<Vec<T>> {
244 let ty = T::from(0).ty();
245 self.0.query_and_then_get_vec(
246 "SELECT i FROM Node WHERE type = ?",
247 (ty,),
248 |a| Ok(a.get(0)?),
249 )
250 }
251 pub fn get_node(&self, id: ID) -> Result<Option<AnyNode>> {
252 AnyNode::from_id(self, id)
253 }
254 pub fn get_located_node(&self, id: ID) -> Result<Option<AnyLocatedNode>> {
255 AnyLocatedNode::from_id(self, id)
256 }
257}
258
259#[cfg(test)]
260mod test {
261 use super::*;
262
263 #[test]
264 fn ureq_sources() {
265 let gd = GD::get_with_sources(getter!(ureq)).unwrap();
266 println!("{} {}", gd.version().unwrap(), gd.timestamp().unwrap());
267 assert!(gd.has_sources().unwrap());
268 }
269
270 #[test]
271 fn ureq_no_sources() {
272 let gd = GD::get_no_sources(getter!(ureq)).unwrap();
273 println!("{} {}", gd.version().unwrap(), gd.timestamp().unwrap());
274 assert!(!gd.has_sources().unwrap());
275 }
276
277 #[tokio::test]
278 async fn reqwest() {
279 GD::get_async_no_sources(getter!(reqwest)).await.unwrap();
280 }
281
282 #[test]
283 fn reqwest_blocking() {
284 GD::get_no_sources(getter!(reqwest_blocking)).unwrap();
285 }
286
287 #[test]
293 fn isahc() {
294 GD::get_no_sources(getter!(isahc)).unwrap();
295 }
296
297 #[tokio::test]
298 async fn isahc_async() {
299 GD::get_async_no_sources(getter!(isahc_async))
300 .await
301 .unwrap();
302 }
303
304 #[test]
310 fn minreq() {
311 GD::get_no_sources(getter!(minreq)).unwrap();
312 }
313
314 #[tokio::test]
315 async fn wreq() {
316 GD::get_async_no_sources(getter!(wreq)).await.unwrap();
317 }
318
319 #[test]
320 fn ehttp() {
321 GD::get_no_sources(getter!(ehttp)).unwrap();
322 }
323
324 #[tokio::test]
325 async fn ehttp_async() {
326 GD::get_async_no_sources(getter!(ehttp_async))
327 .await
328 .unwrap();
329 }
330}