netcdf_reader/nc4/
groups.rs1use std::collections::HashMap;
8
9use hdf5_reader::Hdf5File;
10
11use crate::error::Result;
12use crate::types::{NcDimension, NcGroup};
13
14use super::attributes;
15use super::dimensions;
16use super::variables;
17
18fn leaf_name(name: &str) -> &str {
19 name.rsplit('/').next().unwrap_or(name)
20}
21
22fn visible_dimensions(
23 local_dimensions: &[NcDimension],
24 inherited_dimensions: &[NcDimension],
25) -> Vec<NcDimension> {
26 let mut visible_dimensions = local_dimensions.to_vec();
27 visible_dimensions.extend(
28 inherited_dimensions
29 .iter()
30 .filter(|dim| {
31 !local_dimensions
32 .iter()
33 .any(|local_dim| local_dim.name == dim.name)
34 })
35 .cloned(),
36 );
37 visible_dimensions
38}
39
40fn visible_dim_addr_map(
41 local_dim_addr_map: HashMap<u64, NcDimension>,
42 inherited_dim_addr_map: &HashMap<u64, NcDimension>,
43) -> HashMap<u64, NcDimension> {
44 let mut visible_dim_addr_map = inherited_dim_addr_map.clone();
45 visible_dim_addr_map.extend(local_dim_addr_map);
46 visible_dim_addr_map
47}
48
49pub fn build_root_group(hdf5: &Hdf5File) -> Result<NcGroup> {
51 let root = hdf5.root_group()?;
52 build_group_recursive(&root, "/", &[], &HashMap::new())
53}
54
55fn build_group_recursive(
57 hdf5_group: &hdf5_reader::group::Group<'_>,
58 name: &str,
59 inherited_dimensions: &[NcDimension],
60 inherited_dim_addr_map: &HashMap<u64, NcDimension>,
61) -> Result<NcGroup> {
62 let (hdf5_children, datasets) = hdf5_group.members()?;
63
64 let (local_dimensions, local_dim_addr_map) =
68 dimensions::extract_dimensions_from_datasets(&datasets)?;
69 let visible_dimensions = visible_dimensions(&local_dimensions, inherited_dimensions);
70 let visible_dim_addr_map = visible_dim_addr_map(local_dim_addr_map, inherited_dim_addr_map);
71
72 let variables = variables::extract_variables_from_datasets(
74 &datasets,
75 hdf5_group,
76 &visible_dimensions,
77 &visible_dim_addr_map,
78 )?;
79
80 let nc_attributes = attributes::extract_group_attributes(hdf5_group)?;
82
83 let mut child_groups = Vec::new();
85 for child in &hdf5_children {
86 let child_name = leaf_name(child.name()).to_string();
87 let nc_child = build_group_recursive(
88 child,
89 &child_name,
90 &visible_dimensions,
91 &visible_dim_addr_map,
92 )?;
93 child_groups.push(nc_child);
94 }
95
96 Ok(NcGroup {
97 name: name.to_string(),
98 dimensions: visible_dimensions,
99 variables,
100 attributes: nc_attributes,
101 groups: child_groups,
102 })
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn test_visible_dimensions_include_inherited_without_duplicates() {
111 let local = vec![NcDimension {
112 name: "y".to_string(),
113 size: 4,
114 is_unlimited: false,
115 }];
116 let inherited = vec![
117 NcDimension {
118 name: "x".to_string(),
119 size: 3,
120 is_unlimited: false,
121 },
122 NcDimension {
123 name: "y".to_string(),
124 size: 99,
125 is_unlimited: true,
126 },
127 ];
128
129 let merged = visible_dimensions(&local, &inherited);
130 let names: Vec<&str> = merged.iter().map(|dim| dim.name.as_str()).collect();
131 assert_eq!(names, vec!["y", "x"]);
132 assert_eq!(merged[0].size, 4);
133 assert!(!merged[0].is_unlimited);
134 }
135
136 #[test]
137 fn test_visible_dim_addr_map_prefers_local_dimensions() {
138 let mut inherited = HashMap::new();
139 inherited.insert(
140 10,
141 NcDimension {
142 name: "x".to_string(),
143 size: 3,
144 is_unlimited: false,
145 },
146 );
147 inherited.insert(
148 20,
149 NcDimension {
150 name: "shared".to_string(),
151 size: 1,
152 is_unlimited: false,
153 },
154 );
155
156 let mut local = HashMap::new();
157 local.insert(
158 20,
159 NcDimension {
160 name: "shared".to_string(),
161 size: 2,
162 is_unlimited: true,
163 },
164 );
165
166 let merged = visible_dim_addr_map(local, &inherited);
167 assert_eq!(merged.len(), 2);
168 assert_eq!(merged.get(&10).unwrap().name, "x");
169 assert_eq!(merged.get(&20).unwrap().size, 2);
170 assert!(merged.get(&20).unwrap().is_unlimited);
171 }
172}