alpaca-trade 0.24.2

Rust client for the Alpaca Trading HTTP API
Documentation
use alpaca_core::QueryWriter;

use crate::Error;

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ListRequest {
    pub start: Option<String>,
    pub end: Option<String>,
}

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ListV3Request {
    pub start: Option<String>,
    pub end: Option<String>,
    pub timezone: Option<String>,
}

impl ListRequest {
    pub(crate) fn into_query(self) -> Result<Vec<(String, String)>, Error> {
        let mut query = QueryWriter::default();
        query.push_opt("start", validate_optional_text("start", self.start)?);
        query.push_opt("end", validate_optional_text("end", self.end)?);
        Ok(query.finish())
    }
}

impl ListV3Request {
    pub(crate) fn into_query(self) -> Result<Vec<(String, String)>, Error> {
        let mut query = QueryWriter::default();
        query.push_opt("start", validate_optional_text("start", self.start)?);
        query.push_opt("end", validate_optional_text("end", self.end)?);
        query.push_opt(
            "timezone",
            validate_optional_text("timezone", self.timezone)?,
        );
        Ok(query.finish())
    }
}

pub(crate) fn validate_market(market: &str) -> Result<String, Error> {
    let trimmed = validate_required_text("market", market)?;
    if trimmed.contains('/') {
        return Err(Error::InvalidRequest(
            "market must not contain `/`".to_owned(),
        ));
    }

    Ok(trimmed)
}

fn validate_optional_text(name: &str, value: Option<String>) -> Result<Option<String>, Error> {
    value
        .map(|value| validate_required_text(name, &value))
        .transpose()
}

fn validate_required_text(name: &str, value: &str) -> Result<String, Error> {
    let trimmed = value.trim();
    if trimmed.is_empty() {
        return Err(Error::InvalidRequest(format!(
            "{name} must not be empty or whitespace-only"
        )));
    }

    Ok(trimmed.to_owned())
}