mod tests;
use std::collections::HashSet;
use std::iter::FromIterator;
use std::rc::Rc;
use crate::data_set::dimension::DimensionSize;
use crate::io::compute_padding_size;
use crate::{is_valid_name, Attribute, DataType, Dimension, InvalidDataSet, NC_MAX_VAR_DIMS};
#[derive(Debug, Clone, PartialEq)]
pub struct Variable {
pub(crate) name: String,
pub(crate) unlimited_dim: Option<Rc<Dimension>>,
pub(crate) dims: Vec<Rc<Dimension>>,
pub(crate) attrs: Vec<Attribute>,
pub(crate) data_type: DataType,
}
impl Variable {
pub(in crate::data_set) fn new(
var_name: &str,
var_dims: Vec<Rc<Dimension>>,
data_type: DataType,
) -> Result<Variable, InvalidDataSet> {
Variable::check_var_name(var_name)?;
let unlimited_dim: Option<Rc<Dimension>> = match var_dims.first() {
None => None,
Some(first_dim) => match first_dim.is_unlimited() {
false => None,
true => Some(Rc::clone(first_dim)),
},
};
Variable::check_dims_validity(var_name, &var_dims)?;
Ok(Variable {
name: var_name.to_string(),
unlimited_dim,
dims: var_dims,
attrs: vec![],
data_type,
})
}
pub fn name(&self) -> &str {
&self.name
}
pub fn data_type(&self) -> DataType {
self.data_type.clone()
}
pub fn len(&self) -> usize {
self.num_chunks() * self.chunk_len()
}
pub fn is_empty(&self) -> bool {
self.num_chunks() == 0 || self.chunk_len() == 0
}
pub fn use_dim(&self, dim_name: &str) -> bool {
self.dims.iter().any(|dim| *dim.name.borrow() == dim_name)
}
pub fn num_dims(&self) -> usize {
self.dims.len()
}
pub fn get_dims(&self) -> Vec<Rc<Dimension>> {
self.dims.clone()
}
pub fn dim_names(&self) -> Vec<String> {
self.dims
.iter()
.map(|dim: &Rc<Dimension>| dim.name().to_string())
.collect()
}
pub fn is_record_var(&self) -> bool {
match self.dims.first() {
None => false,
Some(first_dim) => first_dim.is_unlimited(),
}
}
pub fn num_attrs(&self) -> usize {
self.attrs.len()
}
pub fn has_attr(&self, attr_name: &str) -> bool {
self.find_attr_from_name(attr_name).is_ok()
}
pub fn chunk_len(&self) -> usize {
let skip_len: usize = if self.is_record_var() { 1 } else { 0 };
self.dims
.iter()
.skip(skip_len)
.fold(1, |product, dim| product * dim.size())
}
pub fn chunk_size(&self) -> usize {
let mut chunk_size = self.chunk_len() * self.data_type.size_of();
chunk_size += compute_padding_size(chunk_size);
chunk_size
}
pub fn num_chunks(&self) -> usize {
match self.dims.first() {
None => 1, Some(first_dim) => match &first_dim.size {
DimensionSize::Fixed(_) => 1,
DimensionSize::Unlimited(size) => *size.borrow(),
},
}
}
pub fn get_attrs(&self) -> Vec<&Attribute> {
self.attrs.iter().collect()
}
pub fn get_attr_names(&self) -> Vec<String> {
self.attrs
.iter()
.map(|attr: &Attribute| attr.name().to_string())
.collect()
}
pub fn get_attr(&self, attr_name: &str) -> Option<&Attribute> {
self.find_attr_from_name(attr_name)
.map(|result: (usize, &Attribute)| result.1)
.ok()
}
pub fn get_attr_i8(&self, attr_name: &str) -> Option<&[i8]> {
let attr: &Attribute = self.get_attr(attr_name)?;
attr.get_i8()
}
pub fn get_attr_u8(&self, attr_name: &str) -> Option<&[u8]> {
let attr: &Attribute = self.get_attr(attr_name)?;
attr.get_u8()
}
pub fn get_attr_as_string(&self, attr_name: &str) -> Option<String> {
let attr: &Attribute = self.get_attr(attr_name)?;
attr.get_as_string()
}
pub fn get_attr_i16(&self, attr_name: &str) -> Option<&[i16]> {
let attr: &Attribute = self.get_attr(attr_name)?;
attr.get_i16()
}
pub fn get_attr_i32(&self, attr_name: &str) -> Option<&[i32]> {
let attr: &Attribute = self.get_attr(attr_name)?;
attr.get_i32()
}
pub fn get_attr_f32(&self, attr_name: &str) -> Option<&[f32]> {
let attr: &Attribute = self.get_attr(attr_name)?;
attr.get_f32()
}
pub fn get_attr_f64(&self, attr_name: &str) -> Option<&[f64]> {
let attr: &Attribute = self.get_attr(attr_name)?;
attr.get_f64()
}
fn add_attr(&mut self, new_attr: Attribute) -> Result<(), InvalidDataSet> {
if self.find_attr_from_name(&new_attr.name).is_ok() {
return Err(InvalidDataSet::VariableAttributeAlreadyExists {
var_name: self.name.to_string(),
attr_name: new_attr.name.to_string(),
});
}
self.attrs.push(new_attr);
Ok(())
}
pub fn add_attr_i8(&mut self, attr_name: &str, i8_data: Vec<i8>) -> Result<(), InvalidDataSet> {
let attr: Attribute =
Attribute::new_i8(attr_name, i8_data).map_err(|var_attr_name: String| {
InvalidDataSet::VariableAttributeNameNotValid {
var_name: self.name.to_string(),
attr_name: var_attr_name,
}
})?;
self.add_attr(attr)?;
Ok(())
}
pub fn add_attr_u8(&mut self, attr_name: &str, u8_data: Vec<u8>) -> Result<(), InvalidDataSet> {
let attr: Attribute =
Attribute::new_u8(attr_name, u8_data).map_err(|var_attr_name: String| {
InvalidDataSet::VariableAttributeNameNotValid {
var_name: self.name.to_string(),
attr_name: var_attr_name,
}
})?;
self.add_attr(attr)?;
Ok(())
}
pub fn add_attr_string<T: AsRef<str>>(
&mut self,
attr_name: &str,
str_data: T,
) -> Result<(), InvalidDataSet> {
self.add_attr_u8(attr_name, String::from(str_data.as_ref()).into_bytes())
}
pub fn add_attr_i16(
&mut self,
attr_name: &str,
i16_data: Vec<i16>,
) -> Result<(), InvalidDataSet> {
let attr: Attribute =
Attribute::new_i16(attr_name, i16_data).map_err(|var_attr_name: String| {
InvalidDataSet::VariableAttributeNameNotValid {
var_name: self.name.to_string(),
attr_name: var_attr_name,
}
})?;
self.add_attr(attr)?;
Ok(())
}
pub fn add_attr_i32(
&mut self,
attr_name: &str,
i32_data: Vec<i32>,
) -> Result<(), InvalidDataSet> {
let attr: Attribute =
Attribute::new_i32(attr_name, i32_data).map_err(|var_attr_name: String| {
InvalidDataSet::VariableAttributeNameNotValid {
var_name: self.name.to_string(),
attr_name: var_attr_name,
}
})?;
self.add_attr(attr)?;
Ok(())
}
pub fn add_attr_f32(
&mut self,
attr_name: &str,
f32_data: Vec<f32>,
) -> Result<(), InvalidDataSet> {
let attr: Attribute =
Attribute::new_f32(attr_name, f32_data).map_err(|var_attr_name: String| {
InvalidDataSet::VariableAttributeNameNotValid {
var_name: self.name.to_string(),
attr_name: var_attr_name,
}
})?;
self.add_attr(attr)?;
Ok(())
}
pub fn add_attr_f64(
&mut self,
attr_name: &str,
f64_data: Vec<f64>,
) -> Result<(), InvalidDataSet> {
let attr: Attribute =
Attribute::new_f64(attr_name, f64_data).map_err(|var_attr_name: String| {
InvalidDataSet::VariableAttributeNameNotValid {
var_name: self.name.to_string(),
attr_name: var_attr_name,
}
})?;
self.add_attr(attr)?;
Ok(())
}
pub(in crate::data_set) fn rename_attr(
&mut self,
old_attr_name: &str,
new_attr_name: &str,
) -> Result<(), InvalidDataSet> {
if old_attr_name == new_attr_name {
return Ok(());
}
let renamed_attr_index: usize = self.find_attr_from_name(old_attr_name)?.0;
if self.find_attr_from_name(new_attr_name).is_ok() {
return Err(InvalidDataSet::VariableAttributeAlreadyExists {
var_name: self.name.to_string(),
attr_name: new_attr_name.to_string(),
});
}
Attribute::check_attr_name(new_attr_name).map_err(|var_attr_name: String| {
InvalidDataSet::VariableAttributeNameNotValid {
var_name: self.name.to_string(),
attr_name: var_attr_name.to_string(),
}
})?;
let renamed_attr: &mut Attribute = &mut self.attrs[renamed_attr_index];
renamed_attr.name = new_attr_name.to_string();
Ok(())
}
pub fn remove_attr(&mut self, attr_name: &str) -> Result<Attribute, InvalidDataSet> {
let removed_attr_index: usize = self.find_attr_from_name(attr_name)?.0;
let removed_attr: Attribute = self.attrs.remove(removed_attr_index);
Ok(removed_attr)
}
pub(in crate::data_set) fn find_attr_from_name(
&self,
attr_name: &str,
) -> Result<(usize, &Attribute), InvalidDataSet> {
self.attrs
.iter()
.position(|attr| {
attr.name() == attr_name
})
.map(|index| {
(index, &self.attrs[index])
})
.ok_or(InvalidDataSet::VariableAttributeNotDefined {
var_name: self.name.to_string(),
attr_name: attr_name.to_string(),
})
}
pub(super) fn check_var_name(var_name: &str) -> Result<(), InvalidDataSet> {
match is_valid_name(var_name) {
true => Ok(()),
false => Err(InvalidDataSet::VariableNameNotValid(var_name.to_string())),
}
}
fn check_dims_validity(var_name: &str, dims: &[Rc<Dimension>]) -> Result<(), InvalidDataSet> {
if dims.is_empty() {
return Ok(());
}
if let Some(unlim_dim) = dims
.iter()
.skip(1)
.find(|dim: &&Rc<Dimension>| dim.is_unlimited())
{
let dim_names: Vec<String> =
dims.iter().map(|dim: &Rc<Dimension>| dim.name()).collect();
return Err(InvalidDataSet::UnlimitedDimensionMustBeDefinedFirst {
var_name: var_name.to_string(),
unlim_dim_name: unlim_dim.name(),
get_dim_names: dim_names,
});
}
let mut repeated_dim_names: Vec<String> = vec![];
for (i, ref_dim_1) in dims.iter().enumerate().skip(1) {
let i32ernal_repeated_dim_names: Vec<String> = dims
.iter()
.take(i)
.filter(|ref_dim_2: &&Rc<Dimension>| Rc::ptr_eq(ref_dim_1, ref_dim_2))
.map(|ref_dim_2: &Rc<Dimension>| ref_dim_2.name())
.collect();
repeated_dim_names.extend(i32ernal_repeated_dim_names.into_iter());
}
let repeated_dim_names = HashSet::<String>::from_iter(repeated_dim_names);
if !repeated_dim_names.is_empty() {
let dim_names: Vec<String> =
dims.iter().map(|dim: &Rc<Dimension>| dim.name()).collect();
return Err(InvalidDataSet::DimensionsUsedMultipleTimes {
var_name: var_name.to_string(),
get_dim_names: dim_names,
});
}
if dims.len() > NC_MAX_VAR_DIMS {
return Err(InvalidDataSet::MaximumDimensionsPerVariableExceeded {
var_name: var_name.to_string(),
num_dims: dims.len(),
});
}
Ok(())
}
}