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