#![allow(clippy::similar_names)]
use std::marker::PhantomData;
use std::path;
use netcdf_sys::*;
use super::attribute::{Attribute, AttributeValue};
use super::dimension::{self, Dimension};
use super::error;
use super::group::{Group, GroupMut};
use super::types::{NcTypeDescriptor, NcVariableType};
use super::variable::{Variable, VariableMut};
use crate::group::{get_parent_ncid_and_stem, try_get_ncid, try_get_parent_ncid_and_stem};
use crate::utils::checked_with_lock;
#[derive(Debug)]
#[repr(transparent)]
pub(crate) struct RawFile {
ncid: nc_type,
}
impl RawFile {
fn close(self) -> error::Result<()> {
let ncid = self.ncid;
std::mem::forget(self);
checked_with_lock(|| unsafe { nc_close(ncid) })
}
}
impl Drop for RawFile {
fn drop(&mut self) {
let ncid = self.ncid;
let _err = checked_with_lock(|| unsafe { nc_close(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().to_vec();
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;
#[cfg(feature = "4.9.0")]
const NOATTCREORD = NC_NOATTCREORD;
#[cfg(feature = "4.9.0")]
const NODIMSCALE_ATTACH = NC_NODIMSCALE_ATTACH;
const _ = !0;
}
}
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;
checked_with_lock(|| unsafe { nc_open(f.as_ptr().cast(), options.bits(), &mut ncid) })?;
Ok(File(Self { ncid }))
}
#[cfg(feature = "mpi")]
pub(crate) fn open_par_with(
path: &path::Path,
communicator: mpi_sys::MPI_Comm,
info: mpi_sys::MPI_Info,
options: Options,
) -> error::Result<File> {
let f = get_ffi_from_path(path);
let mut ncid: nc_type = 0;
checked_with_lock(|| unsafe {
netcdf_sys::par::nc_open_par(
f.as_ptr().cast(),
options.bits(),
communicator,
info,
&mut ncid,
)
})?;
Ok(File(Self { ncid }))
}
pub(crate) fn append_with(path: &path::Path, options: Options) -> error::Result<FileMut> {
let file = Self::open_with(path, options | Options::WRITE)?;
Ok(FileMut(file))
}
pub(crate) fn create_with(path: &path::Path, options: Options) -> error::Result<FileMut> {
let f = get_ffi_from_path(path);
let mut ncid: nc_type = -1;
checked_with_lock(|| unsafe { nc_create(f.as_ptr().cast(), options.bits(), &mut ncid) })?;
Ok(FileMut(File(Self { ncid })))
}
#[cfg(feature = "mpi")]
pub(crate) fn create_par_with(
path: &path::Path,
communicator: mpi_sys::MPI_Comm,
info: mpi_sys::MPI_Info,
options: Options,
) -> error::Result<FileMut> {
let f = get_ffi_from_path(path);
let mut ncid: nc_type = -1;
checked_with_lock(|| unsafe {
netcdf_sys::par::nc_create_par(
f.as_ptr().cast(),
options.bits(),
communicator,
info,
&mut ncid,
)
})?;
Ok(FileMut(File(Self { ncid })))
}
#[cfg(feature = "has-mmap")]
pub(crate) fn open_from_memory<'buffer>(
name: Option<&str>,
mem: &'buffer [u8],
) -> error::Result<FileMem<'buffer>> {
let cstr = std::ffi::CString::new(name.unwrap_or("/")).unwrap();
let mut ncid = 0;
checked_with_lock(|| unsafe {
nc_open_mem(
cstr.as_ptr(),
NC_NOWRITE,
mem.len(),
mem.as_ptr().cast_mut().cast(),
&mut ncid,
)
})?;
Ok(FileMem(File(Self { ncid }), PhantomData))
}
}
#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
#[repr(transparent)]
pub struct File(RawFile);
impl File {
pub fn path(&self) -> error::Result<std::path::PathBuf> {
let name: Vec<u8> = {
let mut pathlen = 0;
checked_with_lock(|| unsafe {
nc_inq_path(self.0.ncid, &mut pathlen, std::ptr::null_mut())
})?;
let mut name = vec![0_u8; pathlen + 1];
checked_with_lock(|| unsafe {
nc_inq_path(self.0.ncid, std::ptr::null_mut(), name.as_mut_ptr().cast())
})?;
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;
checked_with_lock(|| unsafe { 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>> {
let (ncid, name) =
super::group::try_get_parent_ncid_and_stem(self.ncid(), name).unwrap()?;
Variable::find_from_name(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>> {
let (ncid, name) = try_get_parent_ncid_and_stem(self.ncid(), name).unwrap()?;
Attribute::find_from_name(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>> {
let (ncid, name) =
super::group::try_get_parent_ncid_and_stem(self.ncid(), name).unwrap()?;
super::dimension::dimension_from_name(ncid, name).unwrap()
}
pub fn dimensions(&self) -> impl Iterator<Item = Dimension<'_>> {
super::dimension::dimensions_from_location(self.ncid())
.unwrap()
.map(Result::unwrap)
}
pub fn dimension_len(&self, name: &str) -> Option<usize> {
let (ncid, name) =
super::group::try_get_parent_ncid_and_stem(self.ncid(), name).unwrap()?;
super::dimension::dimension_from_name(ncid, name)
.unwrap()
.map(|x| x.len())
}
pub fn group<'f>(&'f self, name: &str) -> error::Result<Option<Group<'f>>> {
let (ncid, name) = get_parent_ncid_and_stem(self.ncid(), name)?;
try_get_ncid(ncid, name).map(|ncid: Option<i32>| {
ncid.map(|ncid| Group {
ncid,
_file: PhantomData,
})
})
}
pub fn groups(&self) -> error::Result<impl Iterator<Item = Group<'_>>> {
super::group::groups_at_ncid(self.ncid())
}
pub fn types(&self) -> error::Result<impl Iterator<Item = NcVariableType>> {
super::types::all_at_location(self.ncid()).map(|x| x.map(Result::unwrap))
}
pub fn close(self) -> error::Result<()> {
let Self(file) = self;
file.close()
}
}
#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
#[repr(transparent)]
pub struct FileMut(File);
impl std::ops::Deref for FileMut {
type Target = File;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl FileMut {
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(&mut self) -> error::Result<impl Iterator<Item = GroupMut<'_>>> {
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<AttributeValue>,
{
let (ncid, name) = super::group::get_parent_ncid_and_stem(self.ncid(), name)?;
Attribute::put(ncid, NC_GLOBAL, name, val.into())
}
pub fn add_dimension<'f>(&'f mut self, name: &str, len: usize) -> error::Result<Dimension<'f>> {
let (ncid, name) = super::group::get_parent_ncid_and_stem(self.ncid(), name)?;
super::dimension::add_dimension_at(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>> {
Ok(GroupMut(
Group {
ncid: super::group::add_group_at_path(self.ncid(), name)?,
_file: PhantomData,
},
PhantomData,
))
}
pub fn add_variable<'f, T>(
&'f mut self,
name: &str,
dims: &[&str],
) -> error::Result<VariableMut<'f>>
where
T: NcTypeDescriptor,
{
let (ncid, name) = super::group::get_parent_ncid_and_stem(self.ncid(), name)?;
VariableMut::add_from_str(ncid, &T::type_descriptor(), name, dims)
}
pub fn add_variable_with_type<'f>(
&'f mut self,
name: &str,
dims: &[&str],
typ: &NcVariableType,
) -> error::Result<VariableMut<'f>> {
let (ncid, name) = super::group::get_parent_ncid_and_stem(self.ncid(), name)?;
VariableMut::add_from_str(ncid, typ, name, dims)
}
pub fn add_type<T: NcTypeDescriptor>(&mut self) -> error::Result<nc_type> {
crate::types::add_type(self.ncid(), T::type_descriptor(), false)
}
pub fn add_type_from_descriptor(&mut self, typ: NcVariableType) -> error::Result<nc_type> {
crate::types::add_type(self.ncid(), typ, false)
}
pub fn add_variable_from_identifiers<'f, T>(
&'f mut self,
name: &str,
dims: &[dimension::DimensionIdentifier],
) -> error::Result<VariableMut<'f>>
where
T: NcTypeDescriptor,
{
let (ncid, name) = super::group::get_parent_ncid_and_stem(self.ncid(), name)?;
let Some(xtype) = super::types::find_type(ncid, &T::type_descriptor())? else {
return Err("Type not found at this location".into());
};
super::variable::add_variable_from_identifiers(ncid, name, dims, xtype)
}
pub fn add_variable_from_identifiers_with_type<'f>(
&'f mut self,
name: &str,
dims: &[dimension::DimensionIdentifier],
typ: &NcVariableType,
) -> error::Result<VariableMut<'f>> {
let (ncid, name) = super::group::get_parent_ncid_and_stem(self.ncid(), name)?;
let Some(xtype) = super::types::find_type(ncid, typ)? else {
return Err("Type not found at this location".into());
};
super::variable::add_variable_from_identifiers(ncid, name, dims, xtype)
}
pub fn add_string_variable<'f>(
&mut self,
name: &str,
dims: &[&str],
) -> error::Result<VariableMut<'f>> {
let typ = crate::types::NcVariableType::String;
let (ncid, name) = super::group::get_parent_ncid_and_stem(self.ncid(), name)?;
VariableMut::add_from_str(ncid, &typ, name, dims)
}
pub fn sync(&self) -> error::Result<()> {
checked_with_lock(|| unsafe { netcdf_sys::nc_sync(self.ncid()) })
}
pub fn close(self) -> error::Result<()> {
let Self(File(file)) = self;
file.close()
}
pub fn redef(&mut self) -> error::Result<()> {
checked_with_lock(|| unsafe { netcdf_sys::nc_redef(self.ncid()) })
}
pub fn enddef(&mut self) -> error::Result<()> {
checked_with_lock(|| unsafe { netcdf_sys::nc_enddef(self.ncid()) })
}
}
#[cfg(feature = "has-mmap")]
#[allow(clippy::module_name_repetitions)]
pub struct FileMem<'buffer>(File, std::marker::PhantomData<&'buffer [u8]>);
#[cfg(feature = "has-mmap")]
impl std::ops::Deref for FileMem<'_> {
type Target = File;
fn deref(&self) -> &Self::Target {
&self.0
}
}