readap 0.2.0

A parser for the OpenDAP DAP2 protocol
Documentation
use crate::{
    data::{DataArray, DataValueIterator},
    dds::DdsDataset,
    errors::Error,
    DdsValue,
};

#[derive(Clone, Debug)]
pub struct DodsDataset<'a> {
    pub dds: DdsDataset,
    pub data_bytes: &'a [u8],
}

impl<'a> DodsDataset<'a> {
    pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
        let dods_string = String::from_utf8_lossy(bytes);
        let (_, dds) = DdsDataset::parse(&dods_string).map_err(|_| Error::ParseError)?;

        let binary_data_start = match dods_string.find("Data:\n") {
            Some(p) => Ok(p),
            None => Err(Error::InvalidData),
        }? + 6;

        let data_bytes = &bytes[binary_data_start..];

        Ok(DodsDataset { dds, data_bytes })
    }

    pub fn variables(&self) -> Vec<String> {
        self.dds.values.iter().map(|v| v.name()).collect()
    }

    pub fn variable_index(&self, key: &str) -> Option<usize> {
        self.dds.values.iter().position(|v| v.name() == key)
    }

    pub fn variable_byte_offset(&self, key: &str) -> Option<usize> {
        let position = self.variable_index(key)?;
        let offset = (0usize..position).fold(0, |acc, i| acc + self.dds.values[i].byte_count());
        Some(offset)
    }

    pub fn variable_data_iter(&self, key: &str) -> Result<DataValueIterator, Error> {
        let index = match self.variable_index(key) {
            Some(o) => Ok(o),
            None => Err(Error::ParseError),
        }?;

        let offset = match self.variable_byte_offset(key) {
            Some(o) => Ok(o),
            None => Err(Error::ParseError),
        }?;

        match &self.dds.values[index] {
            DdsValue::Array(a) => DataValueIterator::new(
                &self.data_bytes[offset..offset + a.byte_count()],
                a.data_type.clone(),
            ),
            DdsValue::Grid(g) => DataValueIterator::new(
                &self.data_bytes[offset..offset + g.array.byte_count()],
                g.array.data_type.clone(),
            ),
            DdsValue::Structure(_) => Err(Error::NotImplemented),
            DdsValue::Sequence(_) => Err(Error::NotImplemented),
        }
    }

    pub fn variable_data(&self, key: &str) -> Result<DataArray, Error> {
        let index = match self.variable_index(key) {
            Some(o) => Ok(o),
            None => Err(Error::ParseError),
        }?;

        let offset = match self.variable_byte_offset(key) {
            Some(o) => Ok(o),
            None => Err(Error::ParseError),
        }?;

        let (_, data) = match &self.dds.values[index] {
            DdsValue::Array(a) => DataArray::parse(&self.data_bytes[offset..], a.data_type.clone()),
            DdsValue::Grid(g) => {
                DataArray::parse(&self.data_bytes[offset..], g.array.data_type.clone())
            }
            DdsValue::Structure(_) => return Err(Error::NotImplemented),
            DdsValue::Sequence(_) => return Err(Error::NotImplemented),
        }
        .map_err(|_| Error::ParseError)?;

        Ok(data)
    }

    pub fn variable_coords(&self, key: &str) -> Result<Vec<(String, DataArray)>, Error> {
        let position = match self.variable_byte_offset(key) {
            Some(p) => Ok(p),
            None => Err(Error::ParseError),
        }?;

        let index = match self.variable_index(key) {
            Some(i) => Ok(i),
            None => Err(Error::ParseError),
        }?;

        match &self.dds.values[index] {
            DdsValue::Array(a) => {
                let name = a.name.clone();
                DataArray::parse(&self.data_bytes[position..], a.data_type.clone())
                    .map_err(|_| Error::ParseError)
                    .map(|(_, a)| vec![(name, a)])
            }
            DdsValue::Grid(g) => g
                .coords
                .iter()
                .scan(g.coords_offset(), |acc, c| {
                    let name = c.name.clone();
                    let data =
                        DataArray::parse(&self.data_bytes[position + *acc..], c.data_type.clone())
                            .map_err(|_| Error::ParseError)
                            .map(|(_, a)| (name, a));
                    *acc += c.byte_count();
                    Some(data)
                })
                .collect(),
            DdsValue::Structure(_) => Err(Error::NotImplemented),
            DdsValue::Sequence(_) => Err(Error::NotImplemented),
        }
    }
}