#![allow(clippy::similar_names)]
use super::attribute::{AttrValue, Attribute};
use super::dimension::{self, Dimension};
use super::error;
use super::group::{Group, GroupMut};
use super::variable::{Numeric, Variable, VariableMut};
use netcdf_sys::*;
use std::marker::PhantomData;
use std::path;
#[derive(Debug)]
pub(crate) struct RawFile {
ncid: nc_type,
}
impl Drop for RawFile {
fn drop(&mut self) {
unsafe {
let _err = error::checked(super::with_lock(|| nc_close(self.ncid)));
}
}
}
#[cfg(unix)]
fn get_ffi_from_path(path: &path::Path) -> Vec<u8> {
use std::os::unix::ffi::OsStrExt;
let mut bytes = path
.as_os_str()
.as_bytes()
.iter()
.copied()
.collect::<Vec<u8>>();
bytes.push(0);
bytes
}
#[cfg(not(unix))]
fn get_ffi_from_path(path: &path::Path) -> std::ffi::CString {
std::ffi::CString::new(path.to_str().unwrap()).unwrap()
}
bitflags::bitflags! {
#[derive(Default)]
pub struct Options: nc_type {
const WRITE = NC_WRITE;
const NOCLOBBER = NC_NOCLOBBER;
const DISKLESS = NC_DISKLESS;
const _64BIT_DATA = NC_64BIT_DATA;
const _64BIT_OFFSET = NC_64BIT_OFFSET;
const CLASSIC = NC_CLASSIC_MODEL;
const SHARE = NC_SHARE;
const NETCDF4 = NC_NETCDF4;
const INMEMORY = NC_INMEMORY;
}
}
impl RawFile {
pub(crate) fn open_with(path: &path::Path, options: Options) -> error::Result<File> {
let f = get_ffi_from_path(path);
let mut ncid: nc_type = 0;
unsafe {
error::checked(super::with_lock(|| {
nc_open(f.as_ptr().cast(), options.bits(), &mut ncid)
}))?;
}
Ok(File(Self { ncid }))
}
pub(crate) fn append_with(path: &path::Path, options: Options) -> error::Result<MutableFile> {
let file = Self::open_with(path, options | Options::WRITE)?;
Ok(MutableFile(file))
}
pub(crate) fn create_with(path: &path::Path, options: Options) -> error::Result<MutableFile> {
let f = get_ffi_from_path(path);
let mut ncid: nc_type = -1;
unsafe {
error::checked(super::with_lock(|| {
nc_create(f.as_ptr().cast(), options.bits(), &mut ncid)
}))?;
}
Ok(MutableFile(File(Self { ncid })))
}
#[cfg(feature = "memory")]
pub(crate) fn open_from_memory<'buffer>(
name: Option<&str>,
mem: &'buffer [u8],
) -> error::Result<MemFile<'buffer>> {
let cstr = std::ffi::CString::new(name.unwrap_or("/")).unwrap();
let mut ncid = 0;
unsafe {
error::checked(super::with_lock(|| {
nc_open_mem(
cstr.as_ptr(),
NC_NOWRITE,
mem.len(),
mem.as_ptr() as *const u8 as *mut _,
&mut ncid,
)
}))?;
}
Ok(MemFile(File(Self { ncid }), PhantomData))
}
}
#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct File(RawFile);
impl File {
pub fn path(&self) -> error::Result<std::path::PathBuf> {
let name: Vec<u8> = {
let mut pathlen = 0;
unsafe {
error::checked(super::with_lock(|| {
nc_inq_path(self.0.ncid, &mut pathlen, std::ptr::null_mut())
}))?;
}
let mut name = vec![0_u8; pathlen as usize + 1_usize];
unsafe {
error::checked(super::with_lock(|| {
nc_inq_path(
self.0.ncid,
std::ptr::null_mut(),
name.as_mut_ptr() as *mut _,
)
}))?;
}
name.truncate(pathlen);
name
};
#[cfg(not(unix))]
{
Ok(std::path::PathBuf::from(String::from_utf8(name)?))
}
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
let osstr = std::ffi::OsStr::from_bytes(&name);
Ok(std::path::PathBuf::from(osstr))
}
}
pub fn root(&self) -> Option<Group> {
let mut format = 0;
unsafe { error::checked(super::with_lock(|| nc_inq_format(self.ncid(), &mut format))) }
.unwrap();
match format {
NC_FORMAT_NETCDF4 | NC_FORMAT_NETCDF4_CLASSIC => Some(Group {
ncid: self.ncid(),
_file: PhantomData,
}),
_ => None,
}
}
fn ncid(&self) -> nc_type {
self.0.ncid
}
pub fn variable<'f>(&'f self, name: &str) -> Option<Variable<'f>> {
Variable::find_from_name(self.ncid(), name).unwrap()
}
pub fn variables(&self) -> impl Iterator<Item = Variable> {
super::variable::variables_at_ncid(self.ncid())
.unwrap()
.map(Result::unwrap)
}
pub fn attribute<'f>(&'f self, name: &str) -> Option<Attribute<'f>> {
Attribute::find_from_name(self.ncid(), None, name).unwrap()
}
pub fn attributes(&self) -> impl Iterator<Item = Attribute> {
crate::attribute::AttributeIterator::new(self.0.ncid, None)
.unwrap()
.map(Result::unwrap)
}
pub fn dimension<'f>(&self, name: &str) -> Option<Dimension<'f>> {
super::dimension::dimension_from_name(self.ncid(), name).unwrap()
}
pub fn dimensions(&self) -> impl Iterator<Item = Dimension> {
super::dimension::dimensions_from_location(self.ncid())
.unwrap()
.map(Result::unwrap)
}
pub fn group<'f>(&'f self, name: &str) -> error::Result<Option<Group<'f>>> {
super::group::group_from_name(self.ncid(), name)
}
pub fn groups<'f>(&'f self) -> error::Result<impl Iterator<Item = Group<'f>>> {
super::group::groups_at_ncid(self.ncid())
}
pub fn types(&self) -> error::Result<impl Iterator<Item = super::types::VariableType>> {
super::types::all_at_location(self.ncid()).map(|x| x.map(Result::unwrap))
}
}
#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct MutableFile(File);
impl std::ops::Deref for MutableFile {
type Target = File;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl MutableFile {
pub fn root_mut(&mut self) -> Option<GroupMut> {
self.root().map(|root| GroupMut(root, PhantomData))
}
pub fn variable_mut<'f>(&'f mut self, name: &str) -> Option<VariableMut<'f>> {
self.variable(name).map(|var| VariableMut(var, PhantomData))
}
pub fn variables_mut(&mut self) -> impl Iterator<Item = VariableMut> {
self.variables().map(|var| VariableMut(var, PhantomData))
}
pub fn group_mut<'f>(&'f mut self, name: &str) -> error::Result<Option<GroupMut<'f>>> {
self.group(name)
.map(|g| g.map(|g| GroupMut(g, PhantomData)))
}
pub fn groups_mut<'f>(&'f mut self) -> error::Result<impl Iterator<Item = GroupMut<'f>>> {
self.groups().map(|g| g.map(|g| GroupMut(g, PhantomData)))
}
pub fn add_attribute<'a, T>(&'a mut self, name: &str, val: T) -> error::Result<Attribute<'a>>
where
T: Into<AttrValue>,
{
Attribute::put(self.ncid(), NC_GLOBAL, name, val.into())
}
pub fn add_dimension<'f>(&'f mut self, name: &str, len: usize) -> error::Result<Dimension<'f>> {
super::dimension::add_dimension_at(self.ncid(), name, len)
}
pub fn add_unlimited_dimension(&mut self, name: &str) -> error::Result<Dimension> {
self.add_dimension(name, 0)
}
pub fn add_group<'f>(&'f mut self, name: &str) -> error::Result<GroupMut<'f>> {
GroupMut::add_group_at(self.ncid(), name)
}
pub fn add_variable<'f, T>(
&'f mut self,
name: &str,
dims: &[&str],
) -> error::Result<VariableMut<'f>>
where
T: Numeric,
{
VariableMut::add_from_str(self.ncid(), T::NCTYPE, name, dims)
}
pub fn add_variable_with_type<'f>(
&'f mut self,
name: &str,
dims: &[&str],
typ: &super::types::VariableType,
) -> error::Result<VariableMut<'f>> {
VariableMut::add_from_str(self.ncid(), typ.id(), name, dims)
}
pub fn add_opaque_type<'f>(
&'f mut self,
name: &str,
size: usize,
) -> error::Result<super::types::OpaqueType> {
super::types::OpaqueType::add(self.ncid(), name, size)
}
pub fn add_vlen_type<'f, T: Numeric>(
&'f mut self,
name: &str,
) -> error::Result<super::types::VlenType> {
super::types::VlenType::add::<T>(self.ncid(), name)
}
pub fn add_enum_type<'f, T: Numeric>(
&'f mut self,
name: &str,
mappings: &[(&str, T)],
) -> error::Result<super::types::EnumType> {
super::types::EnumType::add::<T>(self.ncid(), name, mappings)
}
pub fn add_compound_type(
&mut self,
name: &str,
) -> error::Result<super::types::CompoundBuilder> {
super::types::CompoundType::add(self.ncid(), name)
}
pub fn add_string_variable<'f>(
&'f mut self,
name: &str,
dims: &[&str],
) -> error::Result<VariableMut<'f>> {
VariableMut::add_from_str(self.ncid(), NC_STRING, name, dims)
}
pub fn add_variable_from_identifiers<'f, T>(
&'f mut self,
name: &str,
dims: &[dimension::Identifier],
) -> error::Result<VariableMut<'f>>
where
T: Numeric,
{
super::variable::add_variable_from_identifiers(self.ncid(), name, dims, T::NCTYPE)
}
}
#[cfg(feature = "memory")]
#[allow(clippy::module_name_repetitions)]
pub struct MemFile<'buffer>(File, std::marker::PhantomData<&'buffer [u8]>);
#[cfg(feature = "memory")]
impl<'a> std::ops::Deref for MemFile<'a> {
type Target = File;
fn deref(&self) -> &Self::Target {
&self.0
}
}