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}