lxy 0.1.1

A convenient async http and RPC framework in Rust
Documentation
//! Configuration for OpenTelemetry integration.

use serde::Deserialize;

use crate::config::Config;

/// Protocol used for exporting telemetry data.
#[derive(Debug, Clone, Default, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum ExporterProtocol {
  /// gRPC protocol for OTLP
  Grpc,
  /// HTTP protocol for OTLP (default)
  #[default]
  Http,
}

/// Span processor type.
#[derive(Debug, Clone, Default, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum SpanProcessorType {
  /// Simple span processor - exports spans immediately (default)
  #[default]
  Simple,
  /// Batch span processor - batches spans before exporting (more efficient, requires async runtime)
  Batch,
}

/// Configuration for OpenTelemetry integration.
///
/// OpenTelemetry is automatically enabled when the `otel` feature is specified.
///
/// # Example Configuration
///
/// ```toml
/// [otel]
/// service_name = "my-service"
/// endpoint = "http://localhost:4317"
/// protocol = "grpc"
/// processor = "batch"
/// ```
#[derive(Debug, Clone, Deserialize)]
pub struct OtelConfig {
  /// Service name reported to the telemetry backend.
  #[serde(default = "default_service_name")]
  pub service_name: String,

  /// OTLP endpoint URL.
  #[serde(default = "default_endpoint")]
  pub endpoint: String,

  /// Protocol for exporting (grpc or http).
  #[serde(default)]
  pub protocol: ExporterProtocol,

  /// Span processor type (simple or batch).
  #[serde(default)]
  pub processor: SpanProcessorType,
}

impl Default for OtelConfig {
  fn default() -> Self {
    Self {
      service_name: default_service_name(),
      endpoint: default_endpoint(),
      protocol: ExporterProtocol::default(),
      processor: SpanProcessorType::default(),
    }
  }
}

fn default_service_name() -> String {
  "lxy-service".to_string()
}

fn default_endpoint() -> String {
  "http://localhost:4317".to_string()
}

impl Config for OtelConfig {
  fn name() -> &'static str {
    "otel"
  }
}

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

  #[test]
  fn test_otel_config_defaults() {
    let config = OtelConfig::default();
    assert_eq!(config.service_name, "lxy-service");
    assert_eq!(config.endpoint, "http://localhost:4317");
    assert_eq!(config.protocol, ExporterProtocol::Http);
    assert_eq!(config.processor, SpanProcessorType::Simple);
  }

  #[test]
  fn test_otel_config_deserialize() {
    let toml_str = r#"
      service_name = "test-service"
      endpoint = "http://otel-collector:4317"
      protocol = "grpc"
      processor = "batch"
    "#;

    let config: OtelConfig = toml::from_str(toml_str).unwrap();

    assert_eq!(config.service_name, "test-service");
    assert_eq!(config.endpoint, "http://otel-collector:4317");
    assert_eq!(config.protocol, ExporterProtocol::Grpc);
    assert_eq!(config.processor, SpanProcessorType::Batch);
  }

  #[test]
  fn test_otel_config_deserialize_http_protocol() {
    let toml_str = r#"
      service_name = "http-service"
      endpoint = "http://localhost:4318"
      protocol = "http"
      processor = "simple"
    "#;

    let config: OtelConfig = toml::from_str(toml_str).unwrap();

    assert_eq!(config.protocol, ExporterProtocol::Http);
    assert_eq!(config.processor, SpanProcessorType::Simple);
  }

  #[test]
  fn test_otel_config_partial_deserialize() {
    let toml_str = r#"
      service_name = "minimal-service"
    "#;

    let config: OtelConfig = toml::from_str(toml_str).unwrap();

    assert_eq!(config.service_name, "minimal-service");
    assert_eq!(config.endpoint, "http://localhost:4317");
    assert_eq!(config.protocol, ExporterProtocol::Http);
    assert_eq!(config.processor, SpanProcessorType::Simple);
  }
}