1use std::sync::OnceLock;
2
3use ndarray::{Array1, Array2};
4use zarrs::array::{Array, ArrayCreateError};
5use zarrs::storage::ReadableListableStorage;
6
7use crate::dtype::read_subset_as_f32;
8use crate::error::{EcpError, Result, ResultExt};
9
10pub struct Node {
13 store: ReadableListableStorage,
14 pub group_path: String,
16 pub child_key: String,
18 embeddings: OnceLock<Result<Option<Array2<f32>>>>,
19 children: OnceLock<Result<Option<Array1<u32>>>>,
20}
21
22fn open_if_present(
25 store: &ReadableListableStorage,
26 path: &str,
27) -> Result<Option<Array<dyn zarrs::storage::ReadableListableStorageTraits>>> {
28 match Array::open(store.clone(), path) {
29 Ok(array) => Ok(Some(array)),
30 Err(ArrayCreateError::MissingMetadata) => Ok(None),
31 Err(e) => Err(EcpError::Store(format!("failed to open {path}: {e}"))),
32 }
33}
34
35impl Node {
36 pub fn new(store: ReadableListableStorage, group_path: String, child_key: String) -> Self {
39 Node {
40 store,
41 group_path,
42 child_key,
43 embeddings: OnceLock::new(),
44 children: OnceLock::new(),
45 }
46 }
47
48 pub fn embeddings(&self) -> Result<Option<&Array2<f32>>> {
52 let result = self.embeddings.get_or_init(|| {
53 let embeddings_path = format!("{}/embeddings", self.group_path);
54 match open_if_present(&self.store, &embeddings_path)? {
55 Some(array) => Ok(Some(read_subset_as_f32(
56 &array,
57 &array.subset_all(),
58 &embeddings_path,
59 )?)),
60 None => Ok(None),
61 }
62 });
63 match result {
64 Ok(opt) => Ok(opt.as_ref()),
65 Err(e) => Err(e.clone()),
66 }
67 }
68
69 pub fn children(&self) -> Result<Option<&Array1<u32>>> {
73 let result = self.children.get_or_init(|| {
74 let ids_path = format!("{}/{}", self.group_path, self.child_key);
75 match open_if_present(&self.store, &ids_path)? {
76 Some(array) => {
77 let ids = array
78 .retrieve_array_subset::<Array1<u32>>(&array.subset_all())
79 .store_err_with(|| format!("failed to retrieve {ids_path}"))?;
80 Ok(Some(ids))
81 }
82 None => Ok(None),
83 }
84 });
85 match result {
86 Ok(opt) => Ok(opt.as_ref()),
87 Err(e) => Err(e.clone()),
88 }
89 }
90
91 pub fn is_loaded(&self) -> bool {
94 self.embeddings
95 .get()
96 .is_some_and(|r| matches!(r, Ok(Some(_))))
97 || self
98 .children
99 .get()
100 .is_some_and(|r| matches!(r, Ok(Some(_))))
101 }
102
103 pub fn resident_bytes(&self) -> usize {
106 let emb_bytes = self
107 .embeddings
108 .get()
109 .and_then(|r| r.as_ref().ok())
110 .and_then(Option::as_ref)
111 .map_or(0, |e| e.len() * size_of::<f32>());
112 let child_bytes = self
113 .children
114 .get()
115 .and_then(|r| r.as_ref().ok())
116 .and_then(Option::as_ref)
117 .map_or(0, |c| c.len() * size_of::<u32>());
118 emb_bytes + child_bytes
119 }
120}
121
122#[cfg(test)]
123#[path = "utests/node.rs"]
124mod tests;