kachaka_api/
shelf_location_resolver.rs1use 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}