Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 37 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ members = [
"libdd-ddsketch-ffi",
"libdd-tinybytes",
"libdd-dogstatsd-client",
"libdd-healthplatform",
"libdd-http-client",
"libdd-log",
"libdd-log-ffi",
Expand Down
47 changes: 47 additions & 0 deletions libdd-healthplatform/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright 2026-Present Datadog, Inc. https://www.datadoghq.com/
# SPDX-License-Identifier: Apache-2.0

[package]
name = "libdd-healthplatform"
version = "0.1.0"
description = "no_std-first Rust types for Datadog's agent-payload healthplatform.proto"
homepage = "https://github.com/DataDog/libdatadog/tree/main/libdd-healthplatform"
repository = "https://github.com/DataDog/libdatadog/tree/main/libdd-healthplatform"
rust-version.workspace = true
edition.workspace = true
license.workspace = true
authors.workspace = true

[lib]
bench = false

[features]
default = []
# Regenerate src/healthplatform.rs from src/pb/*.proto. Off by default; CI builds use checked-in output.
generate-protobuf = ["dep:prost-build", "dep:protoc-bin-vendored"]
# Build the evp_proxy example. Pulls in std + HTTP stack.
example-client = ["dep:reqwest", "dep:tokio", "dep:anyhow", "dep:serde_json"]

[dependencies]
# default-features = false drops the std feature; prost-generated code only needs alloc + derive.
prost = { version = "0.14.3", default-features = false, features = ["derive"] }
prost-types = { version = "0.14.3", default-features = false }

# Example-only deps -- gated. The example POSTs to a localhost trace-agent over plain HTTP, no TLS needed.
reqwest = { version = "0.13.2", default-features = false, optional = true }
tokio = { version = "1.43", features = ["macros", "rt-multi-thread"], optional = true }
anyhow = { version = "1.0", optional = true }
serde_json = { version = "1.0", optional = true }

[build-dependencies]
prost-build = { version = "0.14.3", optional = true }
protoc-bin-vendored = { version = "3.2", optional = true }

[dev-dependencies]
# std + prost-with-std for round-trip tests.
prost = "0.14.3"
prost-types = "0.14.3"

[[example]]
name = "evp_proxy_send"
required-features = ["example-client"]
26 changes: 26 additions & 0 deletions libdd-healthplatform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# libdd-healthplatform

`no_std + alloc` Rust types for Datadog's [`agent-payload` healthplatform protobuf schema][upstream-proto].
Encode and decode are provided by the `prost::Message` trait that every generated type already implements
— bring `prost::Message` into scope on the consumer side and call `encode_to_vec()` / `decode(bytes)`.

## Regenerating bindings

`src/healthplatform.rs` is checked in. Re-generate it whenever `src/pb/healthplatform.proto` changes:

```sh
cargo build -p libdd-healthplatform --features generate-protobuf
```

The vendored `.proto` records the upstream commit SHA in its header — bump it when re-vendoring.

## Example client

A minimal end-to-end demo that POSTs a JSON `HealthReport` through the trace-agent's `evp_proxy` endpoint
lives at `examples/evp_proxy_send.rs`. Build it with:

```sh
cargo build -p libdd-healthplatform --example evp_proxy_send --features example-client
```

[upstream-proto]: https://github.com/DataDog/agent-payload/blob/master/proto/healthplatform/healthplatform.proto
73 changes: 73 additions & 0 deletions libdd-healthplatform/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2026-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

use std::io::Result;

#[cfg(feature = "generate-protobuf")]
use {
std::env,
std::fs::File,
std::io::{Read, Write},
std::path::Path,
};

// to re-generate the protobuf structs, run:
// cargo build -p libdd-healthplatform --features generate-protobuf
fn main() -> Result<()> {
#[cfg(feature = "generate-protobuf")]
{
std::env::set_var("PROTOC", protoc_bin_vendored::protoc_bin_path().unwrap());
generate_protobuf();
}
#[cfg(not(feature = "generate-protobuf"))]
{
println!("cargo:rerun-if-changed=build.rs");
}

Ok(())
}

#[cfg(feature = "generate-protobuf")]
fn generate_protobuf() {
let cur_working_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let current_working_dir_path = Path::new(&cur_working_dir);
let output_path = current_working_dir_path.join("src");

let mut config = prost_build::Config::new();
config.out_dir(output_path.clone());
// Use BTreeMap for all map fields so the generated code stays no_std-clean.
// (HashMap requires the std feature on prost's encoding side.)
config.btree_map(["."]);

// prost-build maps `.google.protobuf` types to `::prost_types` automatically; we
// rely on prost-types' bundled descriptor for google/protobuf/struct.proto so we
// don't have to vendor it ourselves.
config
.compile_protos(&["src/pb/healthplatform.proto"], &["src/pb"])
.unwrap();

// prost-build emits a file named after the proto package: datadog.healthplatform.rs.
// Rename to healthplatform.rs so the include! path in lib.rs stays short.
let generated = output_path.join("datadog.healthplatform.rs");
let target = output_path.join("healthplatform.rs");
std::fs::rename(&generated, &target).unwrap();

let license = b"// Copyright 2026-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0
//
// @generated by prost-build from src/pb/healthplatform.proto.
// Do not edit by hand; run `cargo build -p libdd-healthplatform --features generate-protobuf`.

";
prepend_to_file(license, &target);
}

#[cfg(feature = "generate-protobuf")]
fn prepend_to_file(data: &[u8], file_path: &Path) {
let mut f = File::open(file_path).unwrap();
let mut content = data.to_owned();
f.read_to_end(&mut content).unwrap();

let mut f = File::create(file_path).unwrap();
f.write_all(content.as_slice()).unwrap();
}
Loading
Loading