diff --git a/src/api/mod.rs b/src/api/mod.rs index dc9e9d9..ea7ee96 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -11,7 +11,7 @@ use price::PriceApi; use station::StationApi; use std::sync::Arc; -/// The main struct of the crate giving access to the station and price api of tankerkoenig. +/// The main struct of the crate giving access to the [`StationApi`] and [`PriceApi`] of tankerkoenig. /// Create a new instance of the struct with your api key as parameter. /// /// ## Example diff --git a/src/api/price.rs b/src/api/price.rs index ba6938e..23371e8 100644 --- a/src/api/price.rs +++ b/src/api/price.rs @@ -47,11 +47,9 @@ impl PriceApi { /// Fetch the prices of all fuel types of the given station ids (up to 10 at once). /// - /// ## Further Explanation - /// You can only fetch 10 stations at once. If you want to fetch more than 10 stations, + /// You can only fetch prices for 10 stations at once. If you want to fetch more than 10 stations, /// you have to call this function multiple times. This is due to a limitation of the - /// [tankerkoenig API](https://creativecommons.tankerkoenig.de/). - /// Read more about that on their [website](https://creativecommons.tankerkoenig.de/) + /// [tankerkoenig API](https://creativecommons.tankerkoenig.de/). Use the helper [`macro@chunk_into_option_arrays`](crate::chunk_into_option_arrays) to make this easier. /// /// ## Example /// ``` @@ -64,6 +62,27 @@ impl PriceApi { /// Ok(prices) /// } /// ``` + /// + /// ## Example with [`macro@chunk_into_option_arrays`](crate::chunk_into_option_arrays) + /// ``` + /// use tankerkoenig::Tankerkoenig; + /// use tankerkoenig::models; + /// use tankerkoenig::chunk_into_option_arrays; + /// + /// async fn request_station_prices() -> Result, tankerkoenig::Error> { + /// let tanker = Tankerkoenig::new("your-api-key")?; + /// let station_ids = ["id-1", "id-2", "id-3", "id-4", "id-5", "id-6", "id-7"]; + /// + /// let mut all_prices = Vec::new(); + /// for chunk in chunk_into_option_arrays!(station_ids) { + /// let prices = tanker.price.fetch(&chunk).await?; + /// // Remember to wait between the requests to not get blocked by the API + /// all_prices.push(prices); + /// } + /// Ok(all_prices) + /// } + /// ``` + /// pub async fn fetch( &self, ids: &[Option; MAX_REQUEST_STATION_IDS], @@ -162,22 +181,11 @@ mod test { Pin::from(Box::new(ready(result))) }); + let ids = ["456", "789"]; + let transformed = crate::chunk_into_option_arrays!(ids); + let api = PriceApi::new(Arc::new(Box::new(mock_client)), api_key); - let res = api - .fetch(&[ - Some("456"), - Some("789"), - None, - None, - None, - None, - None, - None, - None, - None, - ]) - .await - .unwrap(); + let res = api.fetch(&transformed.get(0).unwrap()).await.unwrap(); let station_prices: models::price::PriceResponse = serde_json::from_str(&data_string).unwrap(); diff --git a/src/utils.rs b/src/utils.rs index 4d97db7..79131cc 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,43 @@ +/// Macro to transform a vector into a vector of arrays with a fixed size of 10. +/// The last array will be filled with None if the vector is not divisible by 10. +/// +/// # Example +/// ``` +/// use tankerkoenig::chunk_into_option_arrays; +/// +/// let input_vec = vec!["1", "2", "3", "4", "5", "6", "7", "8", "9"]; +/// let output_vec = chunk_into_option_arrays!(input_vec); +/// +/// assert_eq!(output_vec.len(), 1); +/// assert_eq!(output_vec[0], [ +/// Some("1".to_string()), +/// Some("2".to_string()), +/// Some("3".to_string()), +/// Some("4".to_string()), +/// Some("5".to_string()), +/// Some("6".to_string()), +/// Some("7".to_string()), +/// Some("8".to_string()), +/// Some("9".to_string()), +/// None]); +/// ``` +/// +#[macro_export] +macro_rules! chunk_into_option_arrays { + ($input_vec:expr) => {{ + $input_vec + .chunks(10) + .map(|chunk| { + let mut current: [Option; 10] = Default::default(); + for (index, value) in chunk.iter().enumerate() { + current[index] = Some((value.as_ref() as &str).to_string()); + } + current + }) + .collect::; 10]>>() + }}; +} + pub(crate) mod price { use std::{cell::RefCell, rc::Rc}; @@ -26,6 +66,74 @@ pub(crate) mod price { } } +#[cfg(test)] +mod macro_test { + + #[test] + fn transform_vector_to_vector_of_single_array() { + let input = vec!["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]; + let output = chunk_into_option_arrays![input]; + assert_eq!( + output, + vec![[ + Some("a".to_string()), + Some("b".to_string()), + Some("c".to_string()), + Some("d".to_string()), + Some("e".to_string()), + Some("f".to_string()), + Some("g".to_string()), + Some("h".to_string()), + Some("i".to_string()), + Some("j".to_string()) + ]] + ); + } + + #[test] + fn transform_vector_to_vector_of_multiple_arrays_filled_with_none() { + let input = vec!["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "f"]; + let output = chunk_into_option_arrays![input]; + assert_eq!( + output, + vec![ + [ + Some("a".to_string()), + Some("b".to_string()), + Some("c".to_string()), + Some("d".to_string()), + Some("e".to_string()), + Some("f".to_string()), + Some("g".to_string()), + Some("h".to_string()), + Some("i".to_string()), + Some("j".to_string()) + ], + [ + Some("f".to_string()), + None, + None, + None, + None, + None, + None, + None, + None, + None + ] + ] + ); + } + + #[test] + fn transform_empty_vector_to_empty_vector() { + let input: Vec<&str> = vec![]; + let output = chunk_into_option_arrays![input]; + let expected: Vec<[Option; 10]> = vec![]; + assert_eq!(output, expected); + } +} + #[cfg(test)] mod price_test { use super::price::*;