Skip to main content

conspire/io/netcdf/base/
mod.rs

1use crate::io::netcdf::{
2    NetCDF,
3    ffi::{
4        NC_64BIT_DATA, NC_FLOAT, NC_GLOBAL, NC_INT, NC_NOFILL, NC_NOWRITE, nc_close, nc_create,
5        nc_def_dim, nc_enddef, nc_get_att_text, nc_inq_attlen, nc_inq_dimid, nc_inq_dimlen,
6        nc_inq_varid, nc_open, nc_put_att_float, nc_put_att_int, nc_put_att_text, nc_set_fill,
7    },
8    nc_lock,
9};
10use std::ffi::{CStr, CString, NulError, c_char, c_int};
11
12impl NetCDF {
13    pub fn close(&mut self) {
14        let _guard = nc_lock();
15        let status = unsafe { nc_close(self.ncid) };
16        assert_eq!(status, 0, "nc_close failed with status={status}");
17    }
18    pub fn create(path: &str) -> Result<Self, NulError> {
19        let path_c_str = CString::new(path)?;
20        let mut ncid = 0;
21        let _guard = nc_lock();
22        let status = unsafe { nc_create(path_c_str.as_ptr(), NC_64BIT_DATA, &mut ncid) };
23        assert_eq!(
24            status, 0,
25            "Might need a new error type to handle errors properly"
26        );
27        let status = unsafe { nc_set_fill(ncid, NC_NOFILL, std::ptr::null_mut()) };
28        assert_eq!(status, 0, "nc_set_fill failed with status={status}");
29        let dimid = 0;
30        let varid = 0;
31        Ok(Self { ncid, dimid, varid })
32    }
33    pub fn open(path: &str) -> Result<Self, NulError> {
34        let path_c_str = CString::new(path)?;
35        let mut ncid = 0;
36        let _guard = nc_lock();
37        let status = unsafe { nc_open(path_c_str.as_ptr(), NC_NOWRITE, &mut ncid) };
38        assert_eq!(status, 0, "nc_open failed for {path} with status={status}");
39        Ok(Self {
40            ncid,
41            dimid: 0,
42            varid: 0,
43        })
44    }
45    pub fn dimension_length(&self, name: &str) -> Result<usize, NulError> {
46        let name_c_str = CString::new(name)?;
47        let mut dimid: c_int = 0;
48        let _guard = nc_lock();
49        let status = unsafe { nc_inq_dimid(self.ncid, name_c_str.as_ptr(), &mut dimid) };
50        assert_eq!(
51            status, 0,
52            "nc_inq_dimid failed for {name} with status={status}"
53        );
54        let mut len: usize = 0;
55        let status = unsafe { nc_inq_dimlen(self.ncid, dimid, &mut len) };
56        assert_eq!(
57            status, 0,
58            "nc_inq_dimlen failed for {name} with status={status}"
59        );
60        Ok(len)
61    }
62    pub fn try_dimension_length(&self, name: &str) -> Result<Option<usize>, NulError> {
63        let name_c_str = CString::new(name)?;
64        let mut dimid: c_int = 0;
65        let _guard = nc_lock();
66        let status = unsafe { nc_inq_dimid(self.ncid, name_c_str.as_ptr(), &mut dimid) };
67        if status != 0 {
68            return Ok(None);
69        }
70        let mut len: usize = 0;
71        let status = unsafe { nc_inq_dimlen(self.ncid, dimid, &mut len) };
72        assert_eq!(
73            status, 0,
74            "nc_inq_dimlen failed for {name} with status={status}"
75        );
76        Ok(Some(len))
77    }
78    pub fn get_variable_attribute_text(
79        &self,
80        variable: &str,
81        attr_name: &str,
82    ) -> Result<String, NulError> {
83        let variable_c_str = CString::new(variable)?;
84        let mut varid: c_int = 0;
85        let _guard = nc_lock();
86        let status = unsafe { nc_inq_varid(self.ncid, variable_c_str.as_ptr(), &mut varid) };
87        assert_eq!(
88            status, 0,
89            "nc_inq_varid failed for {variable} with status={status}"
90        );
91        let attr_c_str = CString::new(attr_name)?;
92        let mut len: usize = 0;
93        let status = unsafe { nc_inq_attlen(self.ncid, varid, attr_c_str.as_ptr(), &mut len) };
94        assert_eq!(
95            status, 0,
96            "nc_inq_attlen failed for {variable}::{attr_name} with status={status}"
97        );
98        let mut buf: Vec<c_char> = vec![0; len];
99        let status =
100            unsafe { nc_get_att_text(self.ncid, varid, attr_c_str.as_ptr(), buf.as_mut_ptr()) };
101        assert_eq!(
102            status, 0,
103            "nc_get_att_text failed for {variable}::{attr_name} with status={status}"
104        );
105        let bytes: Vec<u8> = buf.into_iter().map(|c| c as u8).collect();
106        Ok(String::from_utf8_lossy(&bytes).into_owned())
107    }
108    pub fn define_dimension(&mut self, name: &str, len: usize) -> Result<(), NulError> {
109        let name_c_str = CString::new(name)?;
110        let _guard = nc_lock();
111        let status = unsafe { nc_def_dim(self.ncid, name_c_str.as_ptr(), len, &mut self.dimid) };
112        assert_eq!(
113            status, 0,
114            "nc_def_dim failed for {name} with status={status}"
115        );
116        Ok(())
117    }
118    pub fn end_definition(&mut self) {
119        let _guard = nc_lock();
120        let status = unsafe { nc_enddef(self.ncid) };
121        assert_eq!(status, 0, "nc_enddef failed with status={status}");
122    }
123    pub fn global(&mut self) {
124        self.put_global_float(c"api_version", 8.25);
125        self.put_global_int(c"file_size", 1);
126        self.put_global_int(c"floating_point_word_size", 8);
127        self.put_global_float(c"version", 8.25);
128        let title = format!("{} v{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
129        self.put_global_text(c"title", &title);
130    }
131    fn put_global_float(&mut self, name: &CStr, value: f32) {
132        let _guard = nc_lock();
133        let status =
134            unsafe { nc_put_att_float(self.ncid, NC_GLOBAL, name.as_ptr(), NC_FLOAT, 1, &value) };
135        assert_eq!(status, 0, "nc_put_att_float failed with status={status}");
136    }
137    fn put_global_int(&mut self, name: &CStr, value: i32) {
138        let _guard = nc_lock();
139        let status =
140            unsafe { nc_put_att_int(self.ncid, NC_GLOBAL, name.as_ptr(), NC_INT, 1, &value) };
141        assert_eq!(status, 0, "nc_put_att_int failed with status={status}");
142    }
143    fn put_global_text(&mut self, name: &CStr, value: &str) {
144        let _guard = nc_lock();
145        let status = unsafe {
146            nc_put_att_text(
147                self.ncid,
148                NC_GLOBAL,
149                name.as_ptr(),
150                value.len(),
151                value.as_ptr() as *const c_char,
152            )
153        };
154        assert_eq!(status, 0, "nc_put_att_text failed with status={status}");
155    }
156    pub fn put_variable_attribute_text(
157        &mut self,
158        variable: &str,
159        attr_name: &str,
160        value: &str,
161    ) -> Result<(), NulError> {
162        let variable_c_str = CString::new(variable)?;
163        let mut varid: c_int = 0;
164        let _guard = nc_lock();
165        let status = unsafe { nc_inq_varid(self.ncid, variable_c_str.as_ptr(), &mut varid) };
166        assert_eq!(
167            status, 0,
168            "nc_inq_varid failed for {variable} with status={status}"
169        );
170        put_attribute_text(self, varid, attr_name, value)
171    }
172}
173
174fn put_attribute_text(
175    netcdf: &mut NetCDF,
176    varid: c_int,
177    name: &str,
178    value: &str,
179) -> Result<(), NulError> {
180    let name_c_str = CString::new(name)?;
181    let value_c_str = CString::new(value)?;
182    let status = unsafe {
183        nc_put_att_text(
184            netcdf.ncid,
185            varid,
186            name_c_str.as_ptr(),
187            value_c_str.as_bytes().len(),
188            value_c_str.as_ptr(),
189        )
190    };
191    assert_eq!(
192        status, 0,
193        "nc_put_att_text failed for {name} with status={status}"
194    );
195    Ok(())
196}