alpaca-mock 0.26.0

Executable mock server for alpaca-rust trading and market-data flows
Documentation
use axum::{
    Json,
    extract::{Extension, Path, Query, State},
};
use serde::Deserialize;

use alpaca_trade::{activities::Activity, orders::SortDirection};

use crate::auth::{AuthenticatedAccount, MockHttpError};
use crate::state::{ListActivitiesFilter, MockServerState};

#[derive(Debug, Deserialize, Default)]
pub(crate) struct ListActivitiesQuery {
    activity_types: Option<String>,
    date: Option<String>,
    until: Option<String>,
    after: Option<String>,
    direction: Option<SortDirection>,
    page_size: Option<u32>,
    page_token: Option<String>,
}

pub(crate) async fn activities_list(
    State(state): State<MockServerState>,
    Extension(account): Extension<AuthenticatedAccount>,
    Query(query): Query<ListActivitiesQuery>,
) -> Result<Json<Vec<Activity>>, MockHttpError> {
    Ok(Json(state.list_activities(
        &account.api_key,
        query.into_filter(None),
    )))
}

pub(crate) async fn activities_by_type(
    State(state): State<MockServerState>,
    Extension(account): Extension<AuthenticatedAccount>,
    Path(activity_type): Path<String>,
    Query(query): Query<ListActivitiesQuery>,
) -> Result<Json<Vec<Activity>>, MockHttpError> {
    Ok(Json(state.list_activities(
        &account.api_key,
        query.into_filter(Some(activity_type)),
    )))
}

impl ListActivitiesQuery {
    fn into_filter(self, activity_type: Option<String>) -> ListActivitiesFilter {
        let activity_types = activity_type
            .map(|activity_type| vec![activity_type])
            .or_else(|| {
                self.activity_types.map(|activity_types| {
                    activity_types
                        .split(',')
                        .map(|activity_type| activity_type.trim().to_owned())
                        .filter(|activity_type| !activity_type.is_empty())
                        .collect::<Vec<_>>()
                })
            });

        ListActivitiesFilter {
            activity_types,
            date: self.date,
            until: self.until,
            after: self.after,
            direction: self.direction,
            page_size: self.page_size,
            page_token: self.page_token,
        }
    }
}