Skip to content

Add eckit crate to workspace - #322

Open
Choochmeque wants to merge 11 commits into
developfrom
rust-binding-high-level-crate
Open

Add eckit crate to workspace#322
Choochmeque wants to merge 11 commits into
developfrom
rust-binding-high-level-crate

Conversation

@Choochmeque

@Choochmeque Choochmeque commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Description

Contributor Declaration

By opening this pull request, I affirm the following:

  • All authors agree to the Contributor License Agreement.
  • The code follows the project's coding standards.
  • I have performed self-review and added comments where needed.
  • I have added or updated tests to verify that my changes are effective and functional.
  • I have run all existing tests and confirmed they pass.

🌦️ >> Documentation << 🌦️
https://sites.ecmwf.int/docs/dev-section/eckit/pull-requests/PR-322

@codecov-commenter

codecov-commenter commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 67.60%. Comparing base (bb29250) to head (150889e).
⚠️ Report is 7 commits behind head on develop.

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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/eckit crate with wrappers for Config, DataHandle, Message/MessageReader, and Stream (TCP + in-memory).
  • Adds the eckit crate to the Rust workspace members and workspace dependencies.
  • Adds a small build script to re-export the ECKIT_SYS dependency 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 on i64/u64 in this crate, so this line will not compile. Since new is already checked to be non-negative, cast to u64 directly (or use u64::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 Send here 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.

Comment thread rust/crates/eckit/src/datahandle.rs
Comment thread rust/crates/eckit/src/datahandle.rs
Comment thread rust/crates/eckit/src/datahandle.rs Outdated
Comment on lines +116 to +118
/// 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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 042f28c.

Comment thread rust/crates/eckit/src/config.rs
Comment thread rust/crates/eckit/src/config.rs Outdated
Comment on lines +56 to +59
pub fn as_sys(&self) -> &eckit_sys::MessageWrapper {
// inner is always Some for a valid Message
&self.inner
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 110e0bd.

Comment on lines +159 to +160
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for TcpStream {}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 150889e.

@caraghbiner caraghbiner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semantically okay I think, though flush should not be a NOOP.

Comment thread rust/crates/eckit/src/datahandle.rs Outdated
Comment on lines +317 to +323
/// `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<()> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5428123.

Comment thread rust/crates/eckit/src/datahandle.rs Outdated
}

/// Access the underlying C++ `DataHandleWrapper` for FFI interop.
pub fn inner_mut(&mut self) -> Result<std::pin::Pin<&mut eckit_sys::DataHandleWrapper>> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be pub instead of e.g. pub(crate)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 9be04d6.

Comment thread rust/crates/eckit/src/datahandle.rs Outdated
Comment on lines +62 to +65
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 {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constructs a DataHandle<closed> but what if the raw data handle was already opened?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 4f0ab97.

Comment thread rust/crates/eckit/src/datahandle.rs Outdated
Comment on lines +114 to +123
/// 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))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 042f28c.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()),
    }
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 042f28c.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants