kachaka-api 0.1.0

Unofficial Rust API client library for Kachaka Robot
Documentation
use futures::stream::StreamExt;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;

use crate::kachaka_api;
use crate::KachakaApiClient;

struct LayoutCollection<T> {
    items: Vec<T>,
    id_index: HashMap<String, usize>,
    name_index: HashMap<String, usize>,
}

impl<T> LayoutCollection<T> {
    pub fn new(
        items: Vec<T>,
        get_id: impl Fn(&T) -> String,
        get_name: impl Fn(&T) -> String,
    ) -> Self {
        let mut id_index = HashMap::new();
        let mut name_index = HashMap::new();

        for (idx, item) in items.iter().enumerate() {
            id_index.insert(get_id(item), idx);
            name_index.insert(get_name(item), idx);
        }

        Self {
            items,
            id_index,
            name_index,
        }
    }

    pub fn get_by_id(&self, id: &str) -> Option<&T> {
        self.id_index.get(id).map(|&idx| &self.items[idx])
    }

    pub fn get_by_name(&self, name: &str) -> Option<&T> {
        self.name_index.get(name).map(|&idx| &self.items[idx])
    }
}

struct ShelfLocationResolverState {
    locations_collection: LayoutCollection<kachaka_api::Location>,
    shelves_collection: LayoutCollection<kachaka_api::Shelf>,
}

pub struct ShelfLocationResolver {
    kachaka_api_client: KachakaApiClient,
    state: Arc<RwLock<ShelfLocationResolverState>>,
}

impl ShelfLocationResolver {
    pub fn new(kachaka_api_client: KachakaApiClient) -> Self {
        Self {
            kachaka_api_client,
            state: Arc::new(RwLock::new(ShelfLocationResolverState {
                locations_collection: LayoutCollection::new(
                    Vec::new(),
                    |location| location.id.clone(),
                    |location| location.name.clone(),
                ),
                shelves_collection: LayoutCollection::new(
                    Vec::new(),
                    |shelf| shelf.id.clone(),
                    |shelf| shelf.name.clone(),
                ),
            })),
        }
    }

    pub async fn run_update_loop(&self) {
        let mut locations_stream = self.kachaka_api_client.clone().watch_locations().await;
        let mut shelves_stream = self.kachaka_api_client.clone().watch_shelves().await;

        loop {
            tokio::select! {
                Some(locations) = locations_stream.next() => {
                    let mut state = self.state.write().await;
                    state.locations_collection = LayoutCollection::new(
                        locations.unwrap(),
                        |location| location.id.clone(),
                        |location| location.name.clone(),
                    );
                }
                Some(shelves) = shelves_stream.next() => {
                    let mut state = self.state.write().await;
                    state.shelves_collection = LayoutCollection::new(
                        shelves.unwrap(),
                        |shelf| shelf.id.clone(),
                        |shelf| shelf.name.clone(),
                    );
                }
            }
        }
    }

    pub async fn get_location_by_id(&self, id: &str) -> Option<kachaka_api::Location> {
        let state = self.state.read().await;
        state.locations_collection.get_by_id(id).cloned()
    }

    pub async fn get_location_by_name(&self, name: &str) -> Option<kachaka_api::Location> {
        let state = self.state.read().await;
        state.locations_collection.get_by_name(name).cloned()
    }

    pub async fn get_all_locations(&self) -> Vec<kachaka_api::Location> {
        self.state.read().await.locations_collection.items.clone()
    }

    pub async fn get_shelf_by_id(&self, id: &str) -> Option<kachaka_api::Shelf> {
        let state = self.state.read().await;
        state.shelves_collection.get_by_id(id).cloned()
    }

    pub async fn get_shelf_by_name(&self, name: &str) -> Option<kachaka_api::Shelf> {
        let state = self.state.read().await;
        state.shelves_collection.get_by_name(name).cloned()
    }

    pub async fn get_all_shelves(&self) -> Vec<kachaka_api::Shelf> {
        self.state.read().await.shelves_collection.items.clone()
    }
}