Add eckit crate to workspace - #322
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #322 +/- ##
===========================================
- Coverage 67.60% 67.60% -0.01%
===========================================
Files 1182 1182
Lines 61697 61697
Branches 4667 4667
===========================================
- Hits 41713 41710 -3
- Misses 19984 19987 +3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR adds a new eckit Rust crate to the rust/ workspace, providing a safe, higher-level wrapper API over the existing eckit-sys FFI bindings (configuration, data handles, message reading, and stream serialization).
Changes:
- Introduces the new
rust/crates/eckitcrate with wrappers forConfig,DataHandle,Message/MessageReader, andStream(TCP + in-memory). - Adds the
eckitcrate to the Rust workspace members and workspace dependencies. - Adds a small build script to re-export the
ECKIT_SYSdependency root for downstream consumers.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| rust/crates/eckit/src/lib.rs | New crate root, module wiring, and public re-exports; adds init() helper. |
| rust/crates/eckit/src/error.rs | Defines crate-wide Result and re-exports eckit_sys::Error. |
| rust/crates/eckit/src/config.rs | Safe wrapper around LocalConfiguration with typed get/set and sub-iteration. |
| rust/crates/eckit/src/datahandle.rs | Typestate DataHandle wrapper with Read/Seek/Write integration and factories. |
| rust/crates/eckit/src/message.rs | GRIB message abstraction and streaming reader with lifetime tying to the source handle. |
| rust/crates/eckit/src/stream.rs | Stream trait + TCP/memory stream wrappers and primitive (de)serialization traits. |
| rust/crates/eckit/Cargo.toml | New crate manifest, features mapped to eckit-sys, and build dependencies. |
| rust/crates/eckit/build.rs | Re-exports ECKIT_SYS dep root via bindman-utils. |
| rust/Cargo.toml | Adds crates/eckit to workspace members and workspace dependencies. |
Suppressed comments (2)
rust/crates/eckit/src/datahandle.rs:284
cast_unsigned()is not defined oni64/u64in this crate, so this line will not compile. Sincenewis already checked to be non-negative, cast tou64directly (or useu64::try_from(new)).
if new < 0 {
return Err(std::io::Error::other("seek to negative position"));
}
new.cast_unsigned()
rust/crates/eckit/src/stream.rs:207
- There’s an
unsafe impl Sendhere without a SAFETY rationale, while other types in this crate (e.g.DataHandle,Config) document the safety argument. Adding a short SAFETY comment makes the unsafe contract clearer for maintainers.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for MemoryStream {}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// The C++ side calls back into the Rust reader on each `read()`; no | ||
| /// intermediate buffer or temp file is staged. Forward-only — the | ||
| /// resulting handle cannot be seeked or written to. |
| pub fn as_sys(&self) -> &eckit_sys::MessageWrapper { | ||
| // inner is always Some for a valid Message | ||
| &self.inner | ||
| } |
| #[allow(clippy::non_send_fields_in_send_ty)] | ||
| unsafe impl Send for TcpStream {} |
caraghbiner
left a comment
There was a problem hiding this comment.
Semantically okay I think, though flush should not be a NOOP.
| /// `eckit::DataHandle::flush()` is virtual but its base implementation | ||
| /// throws `NotImplemented`, so most handle types do not actually | ||
| /// support a mid-stream flush. The commit boundary for write-side | ||
| /// handles is [`DataHandle::close`] — call it explicitly to surface | ||
| /// any flush/commit errors. This `Write::flush` impl is therefore a | ||
| /// no-op by design. | ||
| fn flush(&mut self) -> std::io::Result<()> { |
There was a problem hiding this comment.
I definitely don't think "flush" should be a hardcoded to be a noop (is this masking all concrete implementations of flush?). That is a decision for the underlying implementation to make: if it wants to raise a NotImplemented error, then this should error.
Please make sure no other functions are masking the implemented behaviour.
| } | ||
|
|
||
| /// Access the underlying C++ `DataHandleWrapper` for FFI interop. | ||
| pub fn inner_mut(&mut self) -> Result<std::pin::Pin<&mut eckit_sys::DataHandleWrapper>> { |
There was a problem hiding this comment.
Does this need to be pub instead of e.g. pub(crate)?
| impl DataHandle<Closed> { | ||
| /// Create from a raw eckit-sys wrapper. | ||
| #[must_use] | ||
| pub const fn from_raw(inner: eckit_sys::UniquePtr<eckit_sys::DataHandleWrapper>) -> Self { |
There was a problem hiding this comment.
Constructs a DataHandle<closed> but what if the raw data handle was already opened?
| /// Wrap a Rust `std::io::Read` source as a `DataHandle`. | ||
| /// | ||
| /// The C++ side calls back into the Rust reader on each `read()`; no | ||
| /// intermediate buffer or temp file is staged. Forward-only — the | ||
| /// resulting handle cannot be seeked or written to. | ||
| pub fn from_reader<R>(reader: R) -> Result<Self> | ||
| where | ||
| R: std::io::Read + std::io::Seek + Send + 'static, | ||
| { | ||
| let inner = eckit_sys::DataHandleWrapper::from_reader(eckit_sys::make_reader_box(reader)) |
There was a problem hiding this comment.
I am confused about whether this object should be seekable, or not.
Implies we will only ever read:
/// Forward-only — the resulting handle cannot be seeked
Implies we must be able to seek:
R: Read + Seek + Send + 'static
There was a problem hiding this comment.
Are we always reporting IO errors as io::Error::other?
It would be nice to map the eckit errors to matching std::io error kinds if we can. e.g.
fn to_io_error(error: eckit_sys::Error) -> std::io::Error {
use std::io::{Error, ErrorKind};
match error {
eckit_sys::Error::NotImplemented(message) => {
Error::new(ErrorKind::Unsupported, message)
}
// ...
// etc ...
// ....
other => Error::other(other.to_string()),
}
}
…move unused `pin_mut` method.
Description
Contributor Declaration
By opening this pull request, I affirm the following:
🌦️ >> Documentation << 🌦️
https://sites.ecmwf.int/docs/dev-section/eckit/pull-requests/PR-322