Repository: georust/netcdf (language: rust)

Issue:
Reading OpenDAP
I want to apologize in advance if this is not the right place to ask, if I'm asking a "silly" question or if this question has been asked hundreds of times.
I have been porting all of my Python work into Rust with a considerably high degree of success.
Currently, when using netCDF4.Dataset in Python, one can directly open an DAP connection directly and just request the variables needed.
The netcdf docs for Rust mention the capability of opening a DAP connection directly, but I haven't seen examples, and I have tried doing it in Rust with no success. It seems like I'd need to download the netcdf files and open them locally. Is there a way to mimic the "streaming" behavior of the netCDF4.Dataset using georust/netcdf ?
Thanks again!
-J.



Discussion / hints:
It should work like in python, but it requires the dap support to be included in the library linked (netcdf-c). It should be possible to force this by adding `netcdf-sys = { version="*', features =["dap"]}` to your Cargo.toml
Hi @mulimoen 
I have modified my `Cargo.toml` as follows:

```
cargo add netcdf --features static,ndarray
```
```
cargo add netcdf-sys --features dap,static,memio
```
But now I get:
```
 --- stderr
 thread 'main' panicked at /home/jreniel/.cargo/registry/src/index.crates.io-6f17d22bba15001f/netcdf-sys-0.6.0/build.rs:135:13:
 DAP requested but not found in this installation of netCDF
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
Any help is appreciated,
Thanks!

Could you check if `cargo add netcdf-src --features dap` enables dap support? If it does I can fix this in a new (patch) release of `netcdf-sys`
I fixed an incompatability for the static and dap features. Instead of the above, could you run `cargo update netcdf-sys` and try again?
That fixed it, thanks a lot!
You guys are rock stars!


OK, I reopened because although it compiled, I'm still having one issue. This works in Python:
```Python
from netCDF4 import Dataset
nc = Dataset("https://icdc.cen.uni-hamburg.de/thredds/dodsC/ftpthredds/hamtide/k1.hamtide11a.nc")
```

But the same URL won't work with the Rust NetCDF API:
```Rust
use netcdf;
let url = "https://icdc.cen.uni-hamburg.de/thredds/dodsC/ftpthredds/hamtide/k1.hamtide11a.nc";
let nc = netcdf::open(&url.to_string());
dbg!(&nc);
```
Results in:
```
[src/bctides/src/hamtide.rs:56:9] &url.to_string() = "https://icdc.cen.uni-hamburg.de/thredds/dodsC/ftpthredds/hamtide/k1.hamtide11a.nc"
Error:curl error: SSL peer certificate or SSH remote key was not OK
curl error details: 
Warning:oc_open: Could not read url
[src/bctides/src/hamtide.rs:58:9] &nc = Err(
    Netcdf(
        -68,
    ),
)
```

Any ideas on how to fix this?
Thanks!


Fix patch:
diff --git a/.gitmodules b/.gitmodules
index ec11c84..6a011bd 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
 [submodule "netcdf-src/source"]
 	path = netcdf-src/source
-	url = https://github.com/gigainfosystems/netcdf-c
+	url = https://github.com/mulimoen/netcdf-c
diff --git a/netcdf-src/source b/netcdf-src/source
index 3fabcf3..cbf1282 160000
--- a/netcdf-src/source
+++ b/netcdf-src/source
@@ -1,1 +1,1 @@
-Subproject commit 3fabcf34b18bcba578ef7ad13a20ac0320680267
+Subproject commit cbf128203297f5a990f37b8c28f45455292d5e7b
diff --git a/netcdf-sys/build.rs b/netcdf-sys/build.rs
index 9d3e264..35a1e36 100644
--- a/netcdf-sys/build.rs
+++ b/netcdf-sys/build.rs
@@ -284,6 +284,7 @@ fn main() {
         Version::new(4, 8, 1),
         Version::new(4, 9, 0),
         Version::new(4, 9, 1),
+        Version::new(4, 9, 2),
     ];
 
     if !versions.contains(&metaheader.version) {
@@ -305,6 +306,10 @@ fn main() {
                 "cargo:rustc-cfg=feature=\"{}.{}.{}\"",
                 version.major, version.minor, version.patch
             );
+            println!(
+                "cargo:version_\"{}.{}.{}\"=1",
+                version.major, version.minor, version.patch
+            );
         }
     }
     metaheader.emit_feature_flags();
diff --git a/netcdf-sys/src/functions.rs b/netcdf-sys/src/functions.rs
index 0e80e21..65086bf 100644
--- a/netcdf-sys/src/functions.rs
+++ b/netcdf-sys/src/functions.rs
@@ -1728,6 +1728,11 @@ mod netcdf_2 {
         #[cfg(feature = "4.6.2")]
         pub fn nc_initialize() -> c_int;
         pub fn nc_finalize() -> c_int;
+
+        #[cfg(feature = "4.9.2")]
+        pub fn nc_rc_get(key: *const c_char) -> *mut c_char;
+        #[cfg(feature = "4.9.2")]
+        pub fn nc_rc_set(key: *const c_char, value: *const c_char) -> c_int;
     }
 }
 pub use netcdf_2::*;
diff --git a/netcdf/Cargo.toml b/netcdf/Cargo.toml
index 8f32b43..91ebbaa 100644
--- a/netcdf/Cargo.toml
+++ b/netcdf/Cargo.toml
@@ -23,6 +23,7 @@ static = ["netcdf-sys/static"]
 ndarray = { version = "0.15", optional = true }
 netcdf-sys = { workspace = true }
 bitflags = "2.4.2"
+libc = "0.2.154"
 
 [dev-dependencies]
 clap = { version = "4.5.1", features = ["derive"] }
diff --git a/netcdf/build.rs b/netcdf/build.rs
index 1d174dc..6b0d9be 100644
--- a/netcdf/build.rs
+++ b/netcdf/build.rs
@@ -2,4 +2,9 @@ fn main() {
     if std::env::var("DEP_NETCDF_HAS_MMAP").is_ok() {
         println!("cargo:rustc-cfg=feature=\"has-mmap\"");
     }
+    for (env, _value) in std::env::vars() {
+        if let Some(version) = env.strip_prefix("DEP_NETCDF_VERSION_") {
+            println!("cargo:rustc-cfg=feature={version}");
+        }
+    }
 }
diff --git a/netcdf/src/lib.rs b/netcdf/src/lib.rs
index ae9df16..4f51e48 100644
--- a/netcdf/src/lib.rs
+++ b/netcdf/src/lib.rs
@@ -110,6 +110,8 @@ pub(crate) mod error;
 pub(crate) mod extent;
 pub(crate) mod file;
 pub(crate) mod group;
+#[cfg(feature = "4.9.2")]
+pub mod rc;
 pub mod types;
 pub(crate) mod variable;
 
diff --git a/netcdf/src/rc.rs b/netcdf/src/rc.rs
new file mode 100644
index 0000000..e0be69f
--- /dev/null
+++ b/netcdf/src/rc.rs
@@ -0,0 +1,42 @@
+use std::ffi::{c_char, CStr, CString};
+use std::ops::Deref;
+use std::ptr::NonNull;
+
+pub fn set(key: &str, value: &str) -> crate::error::Result<()> {
+    let key = CString::new(key)?;
+    let value = CString::new(value)?;
+    crate::error::checked(crate::with_lock(|| unsafe {
+        netcdf_sys::nc_rc_set(key.as_ptr(), value.as_ptr())
+    }))
+}
+
+#[derive(Debug)]
+pub struct OwnedString {
+    inner: NonNull<c_char>,
+}
+
+impl Deref for OwnedString {
+    type Target = CStr;
+    fn deref(&self) -> &Self::Target {
+        unsafe { CStr::from_ptr(self.inner.as_ptr()) }
+    }
+}
+
+impl Drop for OwnedString {
+    fn drop(&mut self) {
+        unsafe {
+            libc::free(self.inner.as_ptr().cast());
+        }
+    }
+}
+
+pub fn get(key: &str) -> Option<OwnedString> {
+    let key = if let Ok(key) = CString::new(key) {
+        key
+    } else {
+        return None;
+    };
+    let _lock = netcdf_sys::libnetcdf_lock.lock().unwrap();
+    let value = unsafe { netcdf_sys::nc_rc_get(key.as_ptr()) };
+    NonNull::new(value).map(|inner| OwnedString { inner })
+}


Test patch: