Skip to content
Merged
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
1 change: 1 addition & 0 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 libdd-capabilities-impl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ crate-type = ["lib"]
bench = false

[dependencies]
anyhow = "1.0"
bytes = "1"
http = "1"
libdd-capabilities = { path = "../libdd-capabilities", version = "2.1.0" }
Expand Down
37 changes: 37 additions & 0 deletions libdd-capabilities-impl/src/env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2026-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

//! Native environment-variable capability backed by `std::env`.

use std::env::VarError;

use libdd_capabilities::env::{EnvCapability, EnvError};

#[derive(Clone, Debug)]
pub struct NativeEnvCapability;

impl EnvCapability for NativeEnvCapability {
fn new() -> Self {
Self
}

fn get(&self, name: &str) -> Result<Option<String>, EnvError> {
match std::env::var(name) {
Ok(v) => Ok(Some(v)),
Err(VarError::NotPresent) => Ok(None),
Comment thread
Aaalibaba42 marked this conversation as resolved.
Err(VarError::NotUnicode(_)) => Err(EnvError::NotUnicode(name.to_owned())),
Comment thread
Aaalibaba42 marked this conversation as resolved.
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn get_absent_returns_none() {
let cap = NativeEnvCapability;
// A name unlikely to be set in any environment.
assert_eq!(cap.get("LIBDD_CAP_TEST_ABSENT_XYZZY").unwrap(), None);
}
}
32 changes: 21 additions & 11 deletions libdd-capabilities-impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,25 @@
//! traits using platform-native backends (hyper for HTTP, tokio for sleep,
//! etc.). Leaf crates (FFI, benchmarks) pin this type as the generic parameter.

pub mod env;
mod http;
pub mod sleep;

use core::future::Future;
use std::time::Duration;

pub use env::NativeEnvCapability;
pub use http::NativeHttpClient;
use libdd_capabilities::{http::HttpError, MaybeSend};
pub use libdd_capabilities::{HttpClientCapability, LogWriterCapability, SleepCapability};
pub use libdd_capabilities::{
EnvCapability, EnvError, HttpClientCapability, LogWriterCapability, SleepCapability,
};
pub use sleep::NativeSleepCapability;

/// Bundle struct for native platform capabilities.
///
/// Delegates to [`NativeHttpClient`] for HTTP and [`NativeSleepCapability`] for
/// sleep. Task spawning is handled internally by `SharedRuntime`.
/// Delegates to [`NativeHttpClient`] for HTTP and to unit-struct capabilities
/// for the rest. Task spawning is handled internally by `SharedRuntime`.
///
/// Individual capability traits keep minimal per-function bounds (e.g.
/// functions that only need HTTP require just `H: HttpClientCapability`, not the
Expand All @@ -31,6 +35,7 @@ pub use sleep::NativeSleepCapability;
pub struct NativeCapabilities {
http: NativeHttpClient,
sleep: NativeSleepCapability,
env: NativeEnvCapability,
}

impl Default for NativeCapabilities {
Expand All @@ -44,16 +49,14 @@ impl NativeCapabilities {
Self {
http: NativeHttpClient::new_client(),
sleep: NativeSleepCapability,
env: NativeEnvCapability,
}
}
}

impl HttpClientCapability for NativeCapabilities {
fn new_client() -> Self {
Self {
http: NativeHttpClient::new_client(),
sleep: NativeSleepCapability,
}
Self::new()
}

fn request(
Expand All @@ -78,13 +81,20 @@ impl LogWriterCapability for NativeCapabilities {

impl SleepCapability for NativeCapabilities {
fn new() -> Self {
Self {
http: NativeHttpClient::new_client(),
sleep: NativeSleepCapability,
}
Self::new()
}

fn sleep(&self, duration: Duration) -> impl Future<Output = ()> + MaybeSend {
self.sleep.sleep(duration)
}
}

impl EnvCapability for NativeCapabilities {
fn new() -> Self {
Self::new()
}

fn get(&self, name: &str) -> Result<Option<String>, EnvError> {
self.env.get(name)
}
}
31 changes: 31 additions & 0 deletions libdd-capabilities/src/env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2026-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

//! Environment-variable capability trait and error types.
//!
//! Sync: env access is a single map lookup on both native (`std::env`) and
//! wasm (`process.env`).
//!
//! `set` and `unset` are intentionally absent from this trait. libdatadog is
//! embedded in many kinds of runtime, where mutating the process environment
//! very much unsafe. Exposing mutation here would make it trivially easy for
//! callers to corrupt the environment of a multi-threaded host process.

#[derive(Debug, thiserror::Error)]
pub enum EnvError {
#[error("The value of the environment variable `{0}` is not valid UTF-8")]
NotUnicode(String),
#[error("IO error: {0}")]
Io(anyhow::Error),
}

pub trait EnvCapability: Clone + std::fmt::Debug {
fn new() -> Self;

/// Read an env var.
///
/// `Ok(None)` means the variable is unset; `Err(NotUnicode)` means it is
/// set but its value is not valid UTF-8. Callers that treat "missing" and
/// "invalid" the same should collapse both branches explicitly.
fn get(&self, name: &str) -> Result<Option<String>, EnvError>;
}
2 changes: 2 additions & 0 deletions libdd-capabilities/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@

//! Portable capability traits for cross-platform libdatadog.

pub mod env;
pub mod http;
pub mod log_output;
pub mod maybe_send;
pub mod sleep;
pub mod spawn;

pub use self::env::{EnvCapability, EnvError};
pub use self::http::{HttpClientCapability, HttpError};
pub use self::log_output::LogWriterCapability;
pub use self::sleep::SleepCapability;
Expand Down
Loading