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}