netcdf3/data_set/variable.rs
1mod tests;
2
3use std::collections::HashSet;
4use std::iter::FromIterator;
5use std::rc::Rc;
6
7use crate::{is_valid_name, Attribute, DataType, Dimension, InvalidDataSet, NC_MAX_VAR_DIMS};
8use crate::{data_set::dimension::DimensionSize};
9use crate::io::compute_padding_size;
10
11
12/// NetCDF-3 variable
13///
14/// `Variable` instances are managed by the struct [`DataSet`](struct.DataSet.html).
15///
16/// `DataSet`s allow to create, get, remove and rename `Variable`s.
17///
18/// # Examples
19///
20/// ## Create a variable
21///
22/// ```
23/// use netcdf3::{DataSet, Variable, DataType, DimensionType};
24///
25/// const VAR_NAME: &str = "var_1";
26/// const DIM_NAME_1: &str = "dim_1";
27/// const DIM_NAME_2: &str = "dim_2";
28/// const DIM_SIZE_1: usize = 2;
29/// const DIM_SIZE_2: usize = 3;
30/// const DATA_F32: &'static [f32; 6] = &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0];
31/// const DATA_F32_LEN: usize = DATA_F32.len();
32///
33/// assert_eq!(DATA_F32_LEN, DIM_SIZE_1 * DIM_SIZE_2);
34///
35/// // Create a data set
36/// let mut data_set: DataSet = DataSet::new();
37/// // Define 2 dimensions
38/// data_set.set_unlimited_dim(DIM_NAME_1, DIM_SIZE_1).unwrap();
39/// data_set.add_fixed_dim(DIM_NAME_2, DIM_SIZE_2).unwrap();
40/// // Define a `f32` variable
41/// data_set.add_var_f32(VAR_NAME, &[DIM_NAME_1, DIM_NAME_2]).unwrap();
42///
43/// assert_eq!(true, data_set.has_var(VAR_NAME));
44/// let var: &Variable = data_set.get_var(VAR_NAME).unwrap();
45/// assert_eq!(VAR_NAME, var.name());
46/// assert_eq!(true, var.is_record_var());
47/// assert_eq!(2, var.num_dims());
48/// assert_eq!(0, var.num_attrs());
49/// assert_eq!(vec![DIM_NAME_1, DIM_NAME_2], var.dim_names());
50/// assert_eq!(DATA_F32_LEN, var.len());
51/// assert_eq!(DataType::F32, var.data_type());
52///
53/// ```
54///
55/// ## Rename a variable
56///
57/// ```
58/// use netcdf3::{DataSet, DataType};
59/// const VAR_NAME_1: &str = "var_1";
60/// const VAR_NAME_2: &str = "var_2";
61/// const DIM_NAME: &str = "dim_1";
62/// const VAR_DATA: [i32; 4] = [1, 2, 3, 4];
63/// const VAR_DATA_LEN: usize = VAR_DATA.len();
64///
65/// // Create a data set and a variable
66/// let mut data_set: DataSet = DataSet::new();
67/// data_set.add_fixed_dim(DIM_NAME, VAR_DATA_LEN).unwrap();
68/// data_set.add_var_i32::<&str>(VAR_NAME_1, &[DIM_NAME]).unwrap();
69///
70/// assert_eq!(1, data_set.num_vars());
71/// assert_eq!(true, data_set.has_var(VAR_NAME_1));
72/// assert_eq!(Some(VAR_DATA_LEN), data_set.var_len(VAR_NAME_1));
73/// assert_eq!(Some(DataType::I32), data_set.var_data_type(VAR_NAME_1));
74/// assert_eq!(false, data_set.has_var(VAR_NAME_2));
75/// assert_eq!(None, data_set.var_len(VAR_NAME_2));
76/// assert_eq!(None, data_set.var_data_type(VAR_NAME_2));
77///
78/// // Rename the variable
79/// data_set.rename_var(VAR_NAME_1, VAR_NAME_2).unwrap();
80///
81/// assert_eq!(1, data_set.num_vars());
82/// assert_eq!(false, data_set.has_var(VAR_NAME_1));
83/// assert_eq!(None, data_set.var_len(VAR_NAME_1));
84/// assert_eq!(None, data_set.var_data_type(VAR_NAME_1));
85/// assert_eq!(true, data_set.has_var(VAR_NAME_2));
86/// assert_eq!(Some(VAR_DATA_LEN), data_set.var_len(VAR_NAME_2));
87/// assert_eq!(Some(DataType::I32), data_set.var_data_type(VAR_NAME_2));
88/// ```
89///
90/// ## Remove a variable
91///
92/// ```
93/// use netcdf3::{DataSet, DataType};
94///
95/// const DIM_NAME: &str = "dim_1";
96/// const VAR_NAME: &str = "var_1";
97/// const VAR_DATA: [i32; 4] = [1, 2, 3, 4];
98/// const VAR_DATA_LEN: usize = VAR_DATA.len();
99///
100/// // Create a data set and a variable
101/// let mut data_set: DataSet = DataSet::new();
102///
103/// data_set.add_fixed_dim(DIM_NAME, VAR_DATA_LEN).unwrap();
104/// data_set.add_var_i32::<&str>(VAR_NAME, &[DIM_NAME]).unwrap();
105///
106/// assert_eq!(1, data_set.num_vars());
107/// assert_eq!(true, data_set.has_var(VAR_NAME));
108/// assert_eq!(Some(VAR_DATA_LEN), data_set.var_len(VAR_NAME));
109/// assert_eq!(Some(DataType::I32), data_set.var_data_type(VAR_NAME));
110///
111/// // Remove the variable
112/// data_set.remove_var(VAR_NAME).unwrap();
113///
114/// assert_eq!(0, data_set.num_vars());
115/// assert_eq!(false, data_set.has_var(VAR_NAME));
116/// assert_eq!(None, data_set.var_len(VAR_NAME));
117/// assert_eq!(None, data_set.var_data_type(VAR_NAME));
118/// ```
119#[derive(Debug, Clone, PartialEq)]
120pub struct Variable {
121 pub(crate) name: String,
122 pub(crate) unlimited_dim: Option<Rc<Dimension>>,
123 pub(crate) dims: Vec<Rc<Dimension>>,
124 pub(crate) attrs: Vec<Attribute>,
125 pub(crate) data_type: DataType,
126}
127
128impl Variable {
129 pub(in crate::data_set) fn new(var_name: &str, var_dims: Vec<Rc<Dimension>>, data_type: DataType) -> Result<Variable, InvalidDataSet> {
130 // Check if the name of the variable is a valid NetCDF-3 name.
131 let _ = Variable::check_var_name(var_name)?;
132
133 let unlimited_dim: Option<Rc<Dimension>> = match var_dims.first() {
134 None => None,
135 Some(ref first_dim) => match first_dim.is_unlimited() {
136 false => None,
137 true => Some(Rc::clone(first_dim)),
138 },
139 };
140 Variable::check_dims_validity(var_name, &var_dims)?;
141
142 Ok(Variable {
143 name: var_name.to_string(),
144 unlimited_dim: unlimited_dim,
145 dims: var_dims,
146 attrs: vec![],
147 data_type: data_type,
148 // data: None,
149 })
150 }
151
152 /// Return the name of the variable.
153 pub fn name(&self) -> &str {
154 return &self.name;
155 }
156
157 /// Returns the data type of the variable.
158 ///
159 /// # Example
160 ///
161 /// ```
162 /// use netcdf3::{DataSet, Variable, DataType};
163 /// const VAR_NAME: &str = "var_1";
164 ///
165 /// let data_set: DataSet = {
166 /// let mut data_set = DataSet::new();
167 /// data_set.add_var_i32::<&str>(VAR_NAME, &[]).unwrap();
168 /// data_set
169 /// };
170 ///
171 /// let var: &Variable = data_set.get_var(VAR_NAME).unwrap();
172 /// assert_eq!(DataType::I32, var.data_type());
173 /// ```
174 pub fn data_type(&self) -> DataType {
175 return self.data_type.clone();
176 }
177
178 /// Returns the total number of elements.
179 ///
180 /// If the variable is a record variable then `len = num_chunks * chunk_len`.
181 pub fn len(&self) -> usize {
182 return self.num_chunks() * self.chunk_len();
183 }
184
185 pub fn use_dim(&self, dim_name: &str) -> bool {
186 return self.dims.iter().position(|dim| *dim.name.borrow() == dim_name).is_some();
187 }
188
189 /// Returns the number of dimensions (the rank) the the variables
190 pub fn num_dims(&self) -> usize {
191 return self.dims.len();
192 }
193
194 /// Returns the list of the dimensions
195 pub fn get_dims(&self) -> Vec<Rc<Dimension>>
196 {
197 self.dims.clone()
198 }
199
200 /// Returns the list of the dimension names
201 pub fn dim_names(&self) -> Vec<String>
202 {
203 self.dims.iter().map(|dim: &Rc<Dimension>| {
204 dim.name().to_string()
205 }).collect()
206 }
207
208 /// Returns :
209 ///
210 /// - `true` if the variable is defined over the *unlimited size* dimension, then has several records
211 /// - `false` otherwise
212 pub fn is_record_var(&self) -> bool {
213 match self.dims.first() {
214 None => false,
215 Some(first_dim) => first_dim.is_unlimited()
216 }
217 }
218
219 /// Returns the number of attributes.
220 pub fn num_attrs(&self) -> usize {
221 return self.attrs.len();
222 }
223
224 /// Returns :
225 ///
226 /// - `true` if the variable has the attribute
227 /// - `false` if not
228 pub fn has_attr(&self, attr_name: &str) -> bool {
229 return self.find_attr_from_name(attr_name).is_ok();
230 }
231
232 /// Returns the number of elements per chunk.
233 ///
234 /// If the variable id a *fixed-size* variable then `chunk_len = len`.
235 pub fn chunk_len(&self) -> usize
236 {
237 let skip_len: usize = if self.is_record_var() { 1 } else { 0 };
238 self.dims.iter().skip(skip_len).fold(1, |product, dim| {
239 product * dim.size()
240 })
241 }
242
243 /// Returns the size of each chunk (the number of bytes) including the padding bytes.
244 ///
245 /// # Example
246 ///
247 /// ```
248 /// use netcdf3::{DataSet, Variable};
249 ///
250 /// const VAR_I8_NAME: &str = "scalar_var_i8";
251 /// const VAR_U8_NAME: &str = "scalar_var_u8";
252 /// const VAR_I16_NAME: &str = "scalar_var_i16";
253 /// const VAR_I32_NAME: &str = "scalar_var_i32";
254 /// const VAR_F32_NAME: &str = "scalar_var_f32";
255 /// const VAR_F64_NAME: &str = "scalar_var_f64";
256 ///
257 /// let data_set: DataSet = {
258 /// let mut data_set = DataSet::new();
259 /// data_set.add_var_i8::<&str>(VAR_I8_NAME, &[]).unwrap();
260 /// data_set.add_var_u8::<&str>(VAR_U8_NAME, &[]).unwrap();
261 /// data_set.add_var_i16::<&str>(VAR_I16_NAME, &[]).unwrap();
262 /// data_set.add_var_i32::<&str>(VAR_I32_NAME, &[]).unwrap();
263 /// data_set.add_var_f32::<&str>(VAR_F32_NAME, &[]).unwrap();
264 /// data_set.add_var_f64::<&str>(VAR_F64_NAME, &[]).unwrap();
265 /// data_set
266 /// };
267 ///
268 /// let scalar_var_i8: &Variable = data_set.get_var(VAR_I8_NAME).unwrap();
269 /// let scalar_var_u8: &Variable = data_set.get_var(VAR_U8_NAME).unwrap();
270 /// let scalar_var_i16: &Variable = data_set.get_var(VAR_I16_NAME).unwrap();
271 /// let scalar_var_i32: &Variable = data_set.get_var(VAR_I32_NAME).unwrap();
272 /// let scalar_var_f32: &Variable = data_set.get_var(VAR_F32_NAME).unwrap();
273 /// let scalar_var_f64: &Variable = data_set.get_var(VAR_F64_NAME).unwrap();
274 ///
275 /// assert_eq!(4, scalar_var_i8.chunk_size());
276 /// assert_eq!(4, scalar_var_u8.chunk_size());
277 /// assert_eq!(4, scalar_var_i16.chunk_size());
278 /// assert_eq!(4, scalar_var_i32.chunk_size());
279 /// assert_eq!(4, scalar_var_f32.chunk_size());
280 /// assert_eq!(8, scalar_var_f64.chunk_size());
281 /// ```
282 pub fn chunk_size(&self) -> usize {
283 let mut chunk_size = self.chunk_len() * self.data_type.size_of();
284 // append the bytes of the zero padding, if necessary
285 chunk_size += compute_padding_size(chunk_size);
286 return chunk_size
287 }
288
289 /// Returns the number of chunks.
290 pub fn num_chunks(&self) -> usize {
291 match self.dims.first() {
292 None => 1, // Case : a scalar *fixed-size* variable
293 Some(first_dim) => {
294 match &first_dim.size {
295 DimensionSize::Fixed(_) => 1,
296 DimensionSize::Unlimited(size) => *size.borrow(),
297 }
298 }
299 }
300 }
301
302 /// Returns all attributs defined in the dataset or in the variable.
303 pub fn get_attrs(&self) -> Vec<&Attribute> {
304 return self.attrs.iter().collect();
305 }
306
307 /// Returns all attributs defined in the dataset or in the variable.
308 pub fn get_attr_names(&self) -> Vec<String> {
309 return self.attrs.iter().map(|attr: &Attribute| {
310 attr.name().to_string()
311 }).collect();
312 }
313
314 /// Returns a reference counter to the named attribute, return an error if
315 /// the attribute is not already defined.
316 pub fn get_attr(&self, attr_name: &str) -> Option<&Attribute> {
317 return self.find_attr_from_name(attr_name).map(|result: (usize, &Attribute)|{
318 result.1
319 }).ok();
320 }
321
322 /// Returns the attribute value as a `&[i8]`.
323 ///
324 /// Also see the method [Attribute::get_i8](struct.Attribute.html#method.get_i8).
325 pub fn get_attr_i8(&self, attr_name: &str) -> Option<&[i8]> {
326 let attr: &Attribute = self.get_attr(attr_name)?;
327 attr.get_i8()
328 }
329
330 /// Returns the attribute value as a `&[u8]`.
331 ///
332 /// Also see the method [Attribute::get_u8](struct.Attribute.html#method.get_u8).
333 pub fn get_attr_u8(&self, attr_name: &str) -> Option<&[u8]> {
334 let attr: &Attribute = self.get_attr(attr_name)?;
335 attr.get_u8()
336 }
337
338 /// Returns the attribute value as a `String`.
339 ///
340 /// Also see the method [Attribute::get_as_string](struct.Attribute.html#method.get_as_string).
341 pub fn get_attr_as_string(&self, attr_name: &str) -> Option<String> {
342 let attr: &Attribute = self.get_attr(attr_name)?;
343 attr.get_as_string()
344 }
345
346 /// Returns the attribute value as a `&[i16]`.
347 ///
348 /// Also see the method [Attribute::get_i16](struct.Attribute.html#method.get_i16).
349 pub fn get_attr_i16(&self, attr_name: &str) -> Option<&[i16]> {
350 let attr: &Attribute = self.get_attr(attr_name)?;
351 attr.get_i16()
352 }
353
354 /// Returns the attribute value as a `&[i32]`.
355 ///
356 /// Also see the method [Attribute::get_i32](struct.Attribute.html#method.get_i32).
357 pub fn get_attr_i32(&self, attr_name: &str) -> Option<&[i32]> {
358 let attr: &Attribute = self.get_attr(attr_name)?;
359 attr.get_i32()
360 }
361
362 /// Returns the attribute value as a `&[f32]`.
363 ///
364 /// Also see the method [Attribute::get_f32](struct.Attribute.html#method.get_f32).
365 pub fn get_attr_f32(&self, attr_name: &str) -> Option<&[f32]> {
366 let attr: &Attribute = self.get_attr(attr_name)?;
367 attr.get_f32()
368 }
369
370 /// Returns the attribute value as a `&[f64]`.
371 ///
372 /// Also see the method [Attribute::get_f64](struct.Attribute.html#method.get_f64).
373 pub fn get_attr_f64(&self, attr_name: &str) -> Option<&[f64]> {
374 let attr: &Attribute = self.get_attr(attr_name)?;
375 attr.get_f64()
376 }
377
378 /// Appends a new attribute.
379 ///
380 /// An error is returned if an other attribute with the same name has already been added.
381 fn add_attr(&mut self, new_attr: Attribute) -> Result<(), InvalidDataSet> {
382 // Check if an other same name attribute already exists.
383 if self.find_attr_from_name(&new_attr.name).is_ok() {
384 return Err(InvalidDataSet::VariableAttributeAlreadyExists{
385 var_name: self.name.to_string(),
386 attr_name: new_attr.name.to_string(),
387 });
388 }
389 // append the new attribute
390 self.attrs.push(new_attr);
391 return Ok(());
392 }
393
394 /// Append a new `i8` attribute.
395 ///
396 /// An error is returned if an other attribute with the same name has already been added.
397 pub fn add_attr_i8(&mut self, attr_name: &str, i8_data: Vec<i8>) -> Result<(), InvalidDataSet> {
398 let attr: Attribute = Attribute::new_i8(attr_name, i8_data)
399 .map_err(|var_attr_name: String| InvalidDataSet::VariableAttributeNameNotValid{
400 var_name: self.name.to_string(),
401 attr_name: var_attr_name,
402 })?;
403 self.add_attr(attr)?;
404 Ok(())
405 }
406
407 /// Append a new `u8` attribute.
408 ///
409 /// An error is returned if an other attribute with the same name has already been added.
410 pub fn add_attr_u8(&mut self, attr_name: &str, u8_data: Vec<u8>) -> Result<(), InvalidDataSet> {
411 let attr: Attribute = Attribute::new_u8(attr_name, u8_data)
412 .map_err(|var_attr_name: String| InvalidDataSet::VariableAttributeNameNotValid{
413 var_name: self.name.to_string(),
414 attr_name: var_attr_name,
415 })?;
416 self.add_attr(attr)?;
417 Ok(())
418 }
419
420 /// Append a new `u8` attribute.
421 ///
422 /// An error is returned if an other attribute with the same name has already been added.
423 pub fn add_attr_string<T: AsRef<str>>(&mut self, attr_name: &str, str_data: T) -> Result<(), InvalidDataSet> {
424 self.add_attr_u8(attr_name, String::from(str_data.as_ref()).into_bytes())
425 }
426
427
428 /// Append a new `i16` attribute.
429 ///
430 /// An error is returned if an other attribute with the same name has already been added.
431 pub fn add_attr_i16(&mut self, attr_name: &str, i16_data: Vec<i16>) -> Result<(), InvalidDataSet> {
432 let attr: Attribute = Attribute::new_i16(attr_name, i16_data)
433 .map_err(|var_attr_name: String| InvalidDataSet::VariableAttributeNameNotValid{
434 var_name: self.name.to_string(),
435 attr_name: var_attr_name,
436 })?;
437 self.add_attr(attr)?;
438 Ok(())
439 }
440
441 /// Append a new `i32` attribute.
442 ///
443 /// An error is returned if an other attribute with the same name has already been added.
444 pub fn add_attr_i32(&mut self, attr_name: &str, i32_data: Vec<i32>) -> Result<(), InvalidDataSet> {
445 let attr: Attribute = Attribute::new_i32(attr_name, i32_data)
446 .map_err(|var_attr_name: String| InvalidDataSet::VariableAttributeNameNotValid{
447 var_name: self.name.to_string(),
448 attr_name: var_attr_name,
449 })?;
450 self.add_attr(attr)?;
451 Ok(())
452 }
453
454 /// Append a new `f32` attribute.
455 ///
456 /// An error is returned if an other attribute with the same name has already been added.
457 pub fn add_attr_f32(&mut self, attr_name: &str, f32_data: Vec<f32>) -> Result<(), InvalidDataSet> {
458 let attr: Attribute = Attribute::new_f32(attr_name, f32_data)
459 .map_err(|var_attr_name: String| InvalidDataSet::VariableAttributeNameNotValid{
460 var_name: self.name.to_string(),
461 attr_name: var_attr_name,
462 })?;
463 self.add_attr(attr)?;
464 Ok(())
465 }
466
467 /// Append a new `f64` attribute.
468 ///
469 /// An error is returned if an other attribute with the same name has already been added.
470 pub fn add_attr_f64(&mut self, attr_name: &str, f64_data: Vec<f64>) -> Result<(), InvalidDataSet> {
471 let attr: Attribute = Attribute::new_f64(attr_name, f64_data)
472 .map_err(|var_attr_name: String| InvalidDataSet::VariableAttributeNameNotValid{
473 var_name: self.name.to_string(),
474 attr_name: var_attr_name,
475 })?;
476 self.add_attr(attr)?;
477 Ok(())
478 }
479
480 /// Rename an existing attribute.
481 ///
482 /// An error is returned :
483 /// - the `old_attr_name`is not a valid NetCDF-3 name
484 /// - the `old_attr_name` attribute doesn't exist
485 /// - an other `new_attr_name` attribute already exist
486 pub(in crate::data_set) fn rename_attr(&mut self, old_attr_name: &str, new_attr_name: &str) -> Result<(), InvalidDataSet> {
487 if old_attr_name == new_attr_name {
488 return Ok(());
489 }
490 // Check if the `old_attr_name` attribute exists
491 let renamed_attr_index: usize = self.find_attr_from_name(old_attr_name)?.0;
492 // Check if an other `new_attr_name` attribute already exist
493 if self.find_attr_from_name(new_attr_name).is_ok() {
494 return Err(InvalidDataSet::VariableAttributeAlreadyExists{
495 var_name: self.name.to_string(),
496 attr_name: new_attr_name.to_string(),
497 });
498 }
499
500 // Check that `new_attr_name`is a valid NetCDF-3 name
501 Attribute::check_attr_name(new_attr_name)
502 .map_err(|var_attr_name: String| InvalidDataSet::VariableAttributeNameNotValid{
503 var_name: self.name.to_string(),
504 attr_name:var_attr_name.to_string()
505 })?;
506 let renamed_attr: &mut Attribute = &mut self.attrs[renamed_attr_index];
507 renamed_attr.name = new_attr_name.to_string();
508 return Ok(());
509 }
510
511 // Remove the attribute.
512 pub fn remove_attr(&mut self, attr_name: &str) -> Result<Attribute, InvalidDataSet> {
513 let removed_attr_index: usize = self.find_attr_from_name(attr_name)?.0;
514 let removed_attr: Attribute = self.attrs.remove(removed_attr_index);
515 return Ok(removed_attr);
516 }
517
518 /// Find a dataset's attribute from is name.
519 pub(in crate::data_set) fn find_attr_from_name(&self, attr_name: &str) -> Result<(usize, &Attribute), InvalidDataSet> {
520 self.attrs
521 .iter()
522 .position(|attr| {
523 // First find the position
524 attr.name() == attr_name
525 })
526 .map(|index| {
527 // Then get the referance to the attribute
528 return (index, &self.attrs[index]);
529 })
530 .ok_or(InvalidDataSet::VariableAttributeNotDefined{
531 var_name: self.name.to_string(),
532 attr_name: attr_name.to_string(),
533 })
534 }
535
536 pub(super) fn check_var_name(var_name: &str) -> Result<(), InvalidDataSet> {
537 return match is_valid_name(var_name) {
538 true => Ok(()),
539 false => Err(InvalidDataSet::VariableNameNotValid(var_name.to_string())),
540 };
541 }
542
543 fn check_dims_validity(var_name: &str, dims: &Vec<Rc<Dimension>>) -> Result<(), InvalidDataSet> {
544 if dims.is_empty() {
545 return Ok(());
546 }
547 // Check that the optional unlimited dimension is defined at first
548 if let Some(unlim_dim) = dims.iter().skip(1).find(|dim: &&Rc<Dimension>| dim.is_unlimited()) {
549 let dim_names: Vec<String> = dims.iter().map(|dim: &Rc<Dimension>| {
550 dim.name()
551 }).collect();
552 return Err(InvalidDataSet::UnlimitedDimensionMustBeDefinedFirst{
553 var_name: var_name.to_string(),
554 unlim_dim_name: unlim_dim.name(),
555 get_dim_names: dim_names,
556 });
557 }
558 // Check that the same dimension is not used multiple times by the variable
559 let mut repeated_dim_names: Vec<String> = vec![];
560 for (i, ref_dim_1) in dims.iter().enumerate().skip(1) {
561 let i32ernal_repeated_dim_names: Vec<String> = dims
562 .iter()
563 .take(i)
564 .filter(|ref_dim_2: &&Rc<Dimension>| Rc::ptr_eq(ref_dim_1, ref_dim_2))
565 .map(|ref_dim_2: &Rc<Dimension>| ref_dim_2.name())
566 .collect();
567 repeated_dim_names.extend(i32ernal_repeated_dim_names.into_iter());
568 }
569 let repeated_dim_names = HashSet::<String>::from_iter(repeated_dim_names.into_iter());
570 if !repeated_dim_names.is_empty() {
571 let dim_names: Vec<String> = dims.iter().map(|dim: &Rc<Dimension>| {
572 dim.name()
573 }).collect();
574 return Err(InvalidDataSet::DimensionsUsedMultipleTimes{
575 var_name: var_name.to_string(),
576 get_dim_names: dim_names,
577 });
578 }
579 if dims.len() > NC_MAX_VAR_DIMS {
580 return Err(InvalidDataSet::MaximumDimensionsPerVariableExceeded{
581 var_name: var_name.to_string(),
582 num_dims: dims.len(),
583 })
584 }
585 Ok(())
586 }
587}