Skip to main content

molrs_core/
frame_view.rs

1//! Zero-copy borrowed view of a [`Frame`].
2//!
3//! `FrameView<'a>` borrows blocks from a [`Frame`] as [`BlockView`]s without
4//! copying any array data, providing read-only access with the same API surface
5//! as `Frame`.
6
7use std::collections::HashMap;
8
9use crate::block::block_view::BlockView;
10use crate::frame::Frame;
11use crate::region::simbox::SimBox;
12
13/// A borrowed, read-only view of a [`Frame`].
14///
15/// Block keys are `&str` references into the original `Frame`'s key strings.
16/// Block values are [`BlockView`]s that borrow the underlying column data.
17/// `simbox` and `meta` are borrowed by reference.
18pub struct FrameView<'a> {
19    map: HashMap<&'a str, BlockView<'a>>,
20    /// Borrowed simulation box, if present.
21    pub simbox: Option<&'a SimBox>,
22    /// Borrowed metadata map.
23    pub meta: &'a HashMap<String, String>,
24}
25
26impl<'a> FrameView<'a> {
27    /// Construct a FrameView from parts.
28    pub fn from_parts(
29        map: HashMap<&'a str, BlockView<'a>>,
30        simbox: Option<&'a SimBox>,
31        meta: &'a HashMap<String, String>,
32    ) -> Self {
33        Self { map, simbox, meta }
34    }
35
36    /// Gets an immutable reference to the block view for `key` if present.
37    #[inline]
38    pub fn get(&self, key: &str) -> Option<&BlockView<'a>> {
39        self.map.get(key)
40    }
41
42    /// Returns an iterator over `(&str, &BlockView)`.
43    pub fn iter(&self) -> impl Iterator<Item = (&&'a str, &BlockView<'a>)> {
44        self.map.iter()
45    }
46
47    /// Returns an iterator over block keys.
48    pub fn keys(&self) -> impl Iterator<Item = &&'a str> {
49        self.map.keys()
50    }
51
52    /// Returns an iterator over block view references.
53    pub fn values(&self) -> impl Iterator<Item = &BlockView<'a>> {
54        self.map.values()
55    }
56
57    /// Number of blocks in the view.
58    #[inline]
59    pub fn len(&self) -> usize {
60        self.map.len()
61    }
62
63    /// Returns `true` if the view contains no blocks.
64    #[inline]
65    pub fn is_empty(&self) -> bool {
66        self.map.is_empty()
67    }
68
69    /// Returns `true` if the view contains the specified block key.
70    #[inline]
71    pub fn contains_key(&self, key: &str) -> bool {
72        self.map.contains_key(key)
73    }
74
75    /// Creates an owned [`Frame`] by cloning all viewed data.
76    pub fn to_owned(&self) -> Frame {
77        let mut block_map = HashMap::with_capacity(self.map.len());
78        for (&key, block_view) in &self.map {
79            block_map.insert(key.to_string(), block_view.to_owned());
80        }
81        let mut frame = Frame::from_map(block_map);
82        frame.simbox = self.simbox.cloned();
83        frame.meta = self.meta.clone();
84        frame
85    }
86}
87
88impl<'a> From<&'a Frame> for FrameView<'a> {
89    fn from(frame: &'a Frame) -> Self {
90        let mut map = HashMap::with_capacity(frame.len());
91        for (key, block) in frame.iter() {
92            map.insert(key, BlockView::from(block));
93        }
94        FrameView {
95            map,
96            simbox: frame.simbox.as_ref(),
97            meta: &frame.meta,
98        }
99    }
100}
101
102impl std::fmt::Debug for FrameView<'_> {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        let mut debug_struct = f.debug_struct("FrameView");
105
106        let mut blocks_map = std::collections::BTreeMap::new();
107        for (&k, b) in &self.map {
108            blocks_map.insert(k, (b.nrows(), b.len()));
109        }
110        debug_struct.field("blocks", &blocks_map);
111
112        if !self.meta.is_empty() {
113            debug_struct.field("meta", &self.meta);
114        }
115
116        debug_struct.finish()
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use crate::block::Block;
124    use crate::types::{F, I};
125    use ndarray::Array1;
126
127    fn make_frame() -> Frame {
128        let mut frame = Frame::new();
129        let mut atoms = Block::new();
130        atoms
131            .insert("x", Array1::from_vec(vec![1.0 as F, 2.0, 3.0]).into_dyn())
132            .unwrap();
133        atoms
134            .insert("id", Array1::from_vec(vec![10 as I, 20, 30]).into_dyn())
135            .unwrap();
136        frame.insert("atoms", atoms);
137
138        let mut bonds = Block::new();
139        bonds
140            .insert("type", Array1::from_vec(vec![1 as I, 2]).into_dyn())
141            .unwrap();
142        frame.insert("bonds", bonds);
143
144        frame.meta.insert("title".into(), "Test".into());
145        frame
146    }
147
148    #[test]
149    fn test_from_frame() {
150        let frame = make_frame();
151        let view = FrameView::from(&frame);
152        assert_eq!(view.len(), 2);
153        assert!(view.contains_key("atoms"));
154        assert!(view.contains_key("bonds"));
155        assert!(!view.is_empty());
156    }
157
158    #[test]
159    fn test_get_block_view() {
160        let frame = make_frame();
161        let view = FrameView::from(&frame);
162
163        let atoms = view.get("atoms").unwrap();
164        assert_eq!(atoms.nrows(), Some(3));
165        assert!(atoms.get_float("x").is_some());
166    }
167
168    #[test]
169    fn test_meta_borrowed() {
170        let frame = make_frame();
171        let view = FrameView::from(&frame);
172        assert_eq!(view.meta.get("title").unwrap(), "Test");
173    }
174
175    #[test]
176    fn test_simbox_none() {
177        let frame = make_frame();
178        let view = FrameView::from(&frame);
179        assert!(view.simbox.is_none());
180    }
181
182    #[test]
183    fn test_to_owned_roundtrip() {
184        let frame = make_frame();
185        let view = FrameView::from(&frame);
186        let owned = view.to_owned();
187
188        assert_eq!(owned.len(), 2);
189        assert!(owned.contains_key("atoms"));
190        assert!(owned.contains_key("bonds"));
191        assert_eq!(owned.meta.get("title").unwrap(), "Test");
192
193        let atoms = owned.get("atoms").unwrap();
194        assert_eq!(atoms.nrows(), Some(3));
195        assert_eq!(
196            atoms
197                .get_float("x")
198                .unwrap()
199                .as_slice_memory_order()
200                .unwrap(),
201            &[1.0, 2.0, 3.0]
202        );
203    }
204
205    #[test]
206    fn test_iter_keys() {
207        let frame = make_frame();
208        let view = FrameView::from(&frame);
209
210        let keys: Vec<&&str> = view.keys().collect();
211        assert_eq!(keys.len(), 2);
212
213        let mut count = 0;
214        for (_name, _block) in view.iter() {
215            count += 1;
216        }
217        assert_eq!(count, 2);
218    }
219
220    #[test]
221    fn test_zero_copy() {
222        let frame = make_frame();
223        let view = FrameView::from(&frame);
224
225        let orig_ptr = frame.get("atoms").unwrap().get_float("x").unwrap().as_ptr();
226        let view_ptr = view.get("atoms").unwrap().get_float("x").unwrap().as_ptr();
227        assert_eq!(orig_ptr, view_ptr);
228    }
229}