bitculator 1.1.0

Official Rust SDK for the Bitculator Data API
Documentation
/// A bag of query parameters. Keys mirror the API's names (snake_case); build it
/// fluently:
///
/// ```
/// use bitculator::Params;
/// let p = Params::new().set("per_page", 50).set("sort", "-marketcap");
/// ```
#[derive(Debug, Clone, Default)]
pub struct Params(Vec<(String, String)>);

impl Params {
    pub fn new() -> Self {
        Params(Vec::new())
    }

    /// Set a parameter. A `None` value or empty slice is skipped; a repeated key
    /// replaces the previous value.
    pub fn set(mut self, key: &str, value: impl QueryValue) -> Self {
        if let Some(v) = value.to_query() {
            self.0.retain(|(k, _)| k != key);
            self.0.push((key.to_string(), v));
        }
        self
    }

    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub(crate) fn pairs(&self) -> &[(String, String)] {
        &self.0
    }

    pub(crate) fn with_page(&self, page: u32) -> Params {
        self.clone().set("page", page)
    }
}

/// Conversion into a query-string value. Returning `None` omits the parameter.
pub trait QueryValue {
    fn to_query(&self) -> Option<String>;
}

impl QueryValue for &str {
    fn to_query(&self) -> Option<String> {
        Some((*self).to_string())
    }
}

impl QueryValue for String {
    fn to_query(&self) -> Option<String> {
        Some(self.clone())
    }
}

impl QueryValue for bool {
    fn to_query(&self) -> Option<String> {
        Some(if *self { "true" } else { "false" }.to_string())
    }
}

macro_rules! query_value_display {
    ($($t:ty),*) => {
        $(impl QueryValue for $t {
            fn to_query(&self) -> Option<String> { Some(self.to_string()) }
        })*
    };
}
query_value_display!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64);

impl<T: QueryValue> QueryValue for Option<T> {
    fn to_query(&self) -> Option<String> {
        self.as_ref().and_then(QueryValue::to_query)
    }
}

impl QueryValue for Vec<&str> {
    fn to_query(&self) -> Option<String> {
        if self.is_empty() {
            None
        } else {
            Some(self.join(","))
        }
    }
}

impl QueryValue for Vec<String> {
    fn to_query(&self) -> Option<String> {
        if self.is_empty() {
            None
        } else {
            Some(self.join(","))
        }
    }
}