kachaka_api/
shelf_location_resolver.rs

1use futures::stream::StreamExt;
2use std::collections::HashMap;
3use std::sync::Arc;
4use tokio::sync::RwLock;
5
6use crate::kachaka_api;
7use crate::KachakaApiClient;
8
9struct LayoutCollection<T> {
10    items: Vec<T>,
11    id_index: HashMap<String, usize>,
12    name_index: HashMap<String, usize>,
13}
14
15impl<T> LayoutCollection<T> {
16    pub fn new(
17        items: Vec<T>,
18        get_id: impl Fn(&T) -> String,
19        get_name: impl Fn(&T) -> String,
20    ) -> Self {
21        let mut id_index = HashMap::new();
22        let mut name_index = HashMap::new();
23
24        for (idx, item) in items.iter().enumerate() {
25            id_index.insert(get_id(item), idx);
26            name_index.insert(get_name(item), idx);
27        }
28
29        Self {
30            items,
31            id_index,
32            name_index,
33        }
34    }
35
36    pub fn get_by_id(&self, id: &str) -> Option<&T> {
37        self.id_index.get(id).map(|&idx| &self.items[idx])
38    }
39
40    pub fn get_by_name(&self, name: &str) -> Option<&T> {
41        self.name_index.get(name).map(|&idx| &self.items[idx])
42    }
43}
44
45struct ShelfLocationResolverState {
46    locations_collection: LayoutCollection<kachaka_api::Location>,
47    shelves_collection: LayoutCollection<kachaka_api::Shelf>,
48}
49
50pub struct ShelfLocationResolver {
51    kachaka_api_client: KachakaApiClient,
52    state: Arc<RwLock<ShelfLocationResolverState>>,
53}
54
55impl ShelfLocationResolver {
56    pub fn new(kachaka_api_client: KachakaApiClient) -> Self {
57        Self {
58            kachaka_api_client,
59            state: Arc::new(RwLock::new(ShelfLocationResolverState {
60                locations_collection: LayoutCollection::new(
61                    Vec::new(),
62                    |location| location.id.clone(),
63                    |location| location.name.clone(),
64                ),
65                shelves_collection: LayoutCollection::new(
66                    Vec::new(),
67                    |shelf| shelf.id.clone(),
68                    |shelf| shelf.name.clone(),
69                ),
70            })),
71        }
72    }
73
74    pub async fn run_update_loop(&self) {
75        let mut locations_stream = self.kachaka_api_client.clone().watch_locations().await;
76        let mut shelves_stream = self.kachaka_api_client.clone().watch_shelves().await;
77
78        loop {
79            tokio::select! {
80                Some(locations) = locations_stream.next() => {
81                    let mut state = self.state.write().await;
82                    state.locations_collection = LayoutCollection::new(
83                        locations.unwrap(),
84                        |location| location.id.clone(),
85                        |location| location.name.clone(),
86                    );
87                }
88                Some(shelves) = shelves_stream.next() => {
89                    let mut state = self.state.write().await;
90                    state.shelves_collection = LayoutCollection::new(
91                        shelves.unwrap(),
92                        |shelf| shelf.id.clone(),
93                        |shelf| shelf.name.clone(),
94                    );
95                }
96            }
97        }
98    }
99
100    pub async fn get_location_by_id(&self, id: &str) -> Option<kachaka_api::Location> {
101        let state = self.state.read().await;
102        state.locations_collection.get_by_id(id).cloned()
103    }
104
105    pub async fn get_location_by_name(&self, name: &str) -> Option<kachaka_api::Location> {
106        let state = self.state.read().await;
107        state.locations_collection.get_by_name(name).cloned()
108    }
109
110    pub async fn get_all_locations(&self) -> Vec<kachaka_api::Location> {
111        self.state.read().await.locations_collection.items.clone()
112    }
113
114    pub async fn get_shelf_by_id(&self, id: &str) -> Option<kachaka_api::Shelf> {
115        let state = self.state.read().await;
116        state.shelves_collection.get_by_id(id).cloned()
117    }
118
119    pub async fn get_shelf_by_name(&self, name: &str) -> Option<kachaka_api::Shelf> {
120        let state = self.state.read().await;
121        state.shelves_collection.get_by_name(name).cloned()
122    }
123
124    pub async fn get_all_shelves(&self) -> Vec<kachaka_api::Shelf> {
125        self.state.read().await.shelves_collection.items.clone()
126    }
127}