Rust bindings (eccodes-sys crate)#472
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #472 +/- ##
========================================
Coverage 88.54% 88.54%
========================================
Files 850 850
Lines 63274 63274
Branches 11258 11258
========================================
Hits 56024 56024
Misses 7250 7250 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Introduces an initial Rust workspace and a new eccodes-sys crate that provides low-level (bindgen-generated) FFI bindings to the ecCodes C API, along with CI to format/lint/test the Rust code.
Changes:
- Added
eccodes-syscrate scaffolding (lib.rs,Cargo.toml, README) and abuild.rsthat supportsvendoredandsystembuilds and generates bindgen bindings. - Added docs.rs support via a generated stub
bindings.rswhen building on docs.rs. - Added Rust workspace configuration, Rust CI workflow, and repo-level Rust cargo/clippy configuration.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| rust/crates/eccodes-sys/src/lib.rs | Adds crate root that includes generated bindings and sets lint allowances typical for bindgen output. |
| rust/crates/eccodes-sys/src/bindings_stub.rs | Adds stub bindings intended for docs.rs builds when native deps aren’t available. |
| rust/crates/eccodes-sys/README.md | Documents crate purpose and feature flags. |
| rust/crates/eccodes-sys/Cargo.toml | Defines crate metadata, features, and build dependencies. |
| rust/crates/eccodes-sys/build.rs | Implements vendored/system build logic, resource copying, and bindgen generation; emits linker directives. |
| rust/Cargo.toml | Adds a Rust workspace and git-based dependencies for related tooling/crates. |
| .gitignore | Ignores Rust build artifacts and lockfile under rust/. |
| .github/workflows/ci-rust.yml | Adds Rust fmt/clippy/test workflow. |
| .cargo/config.toml | Adds workspace-wide cargo jobs setting and clippy lint configuration via rustflags. |
Comments suppressed due to low confidence (1)
rust/crates/eccodes-sys/Cargo.toml:52
[package.metadata.docs.rs]is currently empty. If the intent is to make docs.rs build without cloning/building native dependencies (as suggested by the docs.rs stub inbuild.rs), consider settingno-default-features = truehere (and any minimalfeatures = [...]needed) so docs.rs doesn’t enablevendoredby default and pull in heavy build dependencies unnecessarily.
[package.metadata.docs.rs]
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| license = "Apache-2.0" | ||
| repository = "https://github.com/ecmwf/eccodes" | ||
| rust-version = "1.90" | ||
| readme = "README.md" |
| license.workspace = true | ||
| repository.workspace = true | ||
| rust-version.workspace = true | ||
| readme.workspace = true |
| @@ -0,0 +1,51 @@ | |||
| [package] | |||
| name = "eccodes-sys" | |||
| version = "2.47.0" | |||
| use std::env; | ||
| use std::path::{Path, PathBuf}; | ||
|
|
||
| const ECCODES_VERSION: &str = "2.47.0"; |
| "-Wclippy::all", | ||
| "-Wclippy::pedantic", | ||
| "-Wclippy::nursery", | ||
| "-Wclippy::unwrap_used", |
| eckit-sys = { git = "ssh://git@github.com/ecmwf/eckit.git", branch = "rust-bindings", default-features = false } | ||
|
|
||
| # Build tools | ||
| bindman = { git = "ssh://git@github.com/ecmwf/bindman.git", branch = "generate_exception_bridge" } | ||
| bindman-build = { git = "ssh://git@github.com/ecmwf/bindman.git", branch = "generate_exception_bridge" } | ||
| bindman-utils = { git = "ssh://git@github.com/ecmwf/bindman.git", branch = "generate_exception_bridge" } |
| pub const PRODUCT_ANY: c_int = 0; | ||
| pub const PRODUCT_GRIB: c_int = 1; | ||
| pub const PRODUCT_BUFR: c_int = 2; | ||
| pub const PRODUCT_METAR: c_int = 3; | ||
| pub const PRODUCT_GTS: c_int = 4; | ||
| pub const PRODUCT_TAF: c_int = 5; | ||
|
|
||
| // ProductKind enum aliases (matches bindgen output) | ||
| pub const ProductKind_PRODUCT_ANY: c_int = PRODUCT_ANY; | ||
| pub const ProductKind_PRODUCT_GRIB: c_int = PRODUCT_GRIB; | ||
| pub const ProductKind_PRODUCT_BUFR: c_int = PRODUCT_BUFR; | ||
| pub const ProductKind_PRODUCT_METAR: c_int = PRODUCT_METAR; | ||
| pub const ProductKind_PRODUCT_GTS: c_int = PRODUCT_GTS; | ||
| pub const ProductKind_PRODUCT_TAF: c_int = PRODUCT_TAF; |
| fn build_vendored(out_dir: &Path) { | ||
| use std::fs; | ||
|
|
||
| const ECBUILD_REPO: &str = "https://github.com/ecmwf/ecbuild.git"; | ||
| const ECBUILD_TAG: &str = "3.13.1"; | ||
|
|
||
| const AEC_REPO: &str = "https://gitlab.dkrz.de/k202009/libaec.git"; | ||
| const AEC_TAG: &str = "v1.1.4"; | ||
|
|
||
| const ECCODES_REPO: &str = "https://github.com/ecmwf/eccodes.git"; | ||
|
|
||
| let src_dir = out_dir.join("src"); | ||
| let build_dir = out_dir.join("build"); | ||
| let install_dir = out_dir.join("install"); | ||
|
|
||
| fs::create_dir_all(&src_dir).expect("Failed to create src directory"); | ||
|
|
||
| // Get eckit paths from dependency | ||
| let eckit_root = env::var("DEP_ECKIT_SYS_ROOT") | ||
| .expect("DEP_ECKIT_SYS_ROOT not set - eckit-sys must be a dependency"); | ||
|
|
||
| // Clone sources | ||
| let ecbuild_src = bindman_utils::git_clone(ECBUILD_REPO, ECBUILD_TAG, &src_dir.join("ecbuild")); | ||
| let aec_src = bindman_utils::git_clone(AEC_REPO, AEC_TAG, &src_dir.join("libaec")); | ||
| let eccodes_src = | ||
| bindman_utils::git_clone(ECCODES_REPO, ECCODES_VERSION, &src_dir.join("eccodes")); |
| const ECCODES_VERSION: &str = "2.47.0"; | ||
|
|
||
| fn main() { | ||
| println!("cargo:rerun-if-changed=build.rs"); |
|
This is a one-to-one port of the C API. I’m wondering whether this is really what we want. Our C API has several drawbacks that have caused us headaches for years, including a lack of library initialisation and finalisation and a vast number of functions. I don’t think we want to carry these problems over to Rust. What we actually want is a Rust-idiomatic API. Therefore, I think we should take the time to design it carefully. |
Description
Contributor Declaration
By opening this pull request, I affirm the following: