ray/
request.rs

1use crate::origin::Origin;
2use crate::RayConfig;
3use serde::Serialize;
4use std::borrow::Cow;
5use std::sync::Arc;
6use uuid::Uuid;
7
8/// A serialized Ray request containing one or more payloads.
9///
10/// ```rust
11/// use ray::{Client, Origin, PayloadEnvelope, RayConfig, Request};
12/// use serde_json::json;
13///
14/// let config = RayConfig {
15///     enabled: false,
16///     ..Default::default()
17/// };
18/// let client = Client::new(config);
19/// let origin = std::sync::Arc::new(Origin::default());
20/// let payload = PayloadEnvelope::new("log", json!({ "value": "hello" }), origin);
21/// let request = Request::new(uuid::Uuid::new_v4(), vec![payload], client.meta().clone());
22///
23/// assert_eq!(request.payloads.len(), 1);
24/// ```
25#[derive(Debug, Clone, Serialize)]
26pub struct Request {
27    pub uuid: Uuid,
28    pub payloads: Vec<PayloadEnvelope>,
29    #[serde(skip_serializing_if = "meta_is_empty")]
30    pub meta: Arc<Meta>,
31}
32
33fn meta_is_empty(meta: &Arc<Meta>) -> bool {
34    meta.is_empty()
35}
36
37impl Request {
38    /// Create a new request envelope.
39    pub fn new(uuid: Uuid, payloads: Vec<PayloadEnvelope>, meta: Arc<Meta>) -> Self {
40        Self {
41            uuid,
42            payloads,
43            meta,
44        }
45    }
46}
47
48/// Envelope for a single Ray payload.
49///
50/// ```rust
51/// use ray::{Origin, PayloadEnvelope};
52/// use serde_json::json;
53///
54/// let origin = std::sync::Arc::new(Origin::default());
55/// let payload = PayloadEnvelope::new("log", json!({ "value": "hello" }), origin);
56/// assert_eq!(payload.type_name.as_ref(), "log");
57/// ```
58#[derive(Debug, Clone, Serialize)]
59pub struct PayloadEnvelope {
60    #[serde(rename = "type")]
61    pub type_name: Cow<'static, str>,
62    pub content: serde_json::Value,
63    pub origin: Arc<Origin>,
64}
65
66impl PayloadEnvelope {
67    /// Create a new payload envelope.
68    pub fn new(
69        type_name: impl Into<Cow<'static, str>>,
70        content: serde_json::Value,
71        origin: Arc<Origin>,
72    ) -> Self {
73        Self {
74            type_name: type_name.into(),
75            content,
76            origin,
77        }
78    }
79}
80
81#[derive(Debug, Clone, Serialize, Default)]
82pub struct Meta {
83    #[serde(skip_serializing_if = "Option::is_none")]
84    pub rustc_version: Option<String>,
85
86    #[serde(skip_serializing_if = "Option::is_none")]
87    pub project_name: Option<String>,
88
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub project_version: Option<&'static str>,
91
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub ray_package_version: Option<&'static str>,
94}
95
96impl Meta {
97    /// Build metadata from a configuration.
98    pub fn from_config(config: &RayConfig) -> Self {
99        if !config.send_meta {
100            return Self::default();
101        }
102
103        let rustc_version = rustc_version::version_meta()
104            .ok()
105            .map(|m| m.semver.to_string());
106
107        Self {
108            rustc_version,
109            project_name: config
110                .project_name
111                .clone()
112                .or_else(|| Some(env!("CARGO_PKG_NAME").to_string())),
113            project_version: Some(env!("CARGO_PKG_VERSION")),
114            ray_package_version: Some(env!("CARGO_PKG_VERSION")),
115        }
116    }
117
118    fn is_empty(&self) -> bool {
119        self.rustc_version.is_none()
120            && self.project_name.is_none()
121            && self.project_version.is_none()
122            && self.ray_package_version.is_none()
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129    use crate::{Client, Origin, PayloadEnvelope, RayConfig};
130    use serde_json::json;
131    use std::num::NonZeroU16;
132    use std::sync::Arc;
133
134    #[test]
135    fn meta_is_omitted_when_send_meta_disabled() {
136        let config = RayConfig {
137            enabled: true,
138            host: "localhost".to_string(),
139            port: NonZeroU16::new(23517).expect("port must be non-zero"),
140            send_meta: false,
141            ..RayConfig::default()
142        };
143        let client = Client::new(config);
144        let origin = Arc::new(Origin::default());
145        let payload = PayloadEnvelope::new("log", json!({}), origin);
146        let request = Request::new(uuid::Uuid::new_v4(), vec![payload], client.meta().clone());
147
148        let serialized = serde_json::to_value(&request).expect("request must serialize");
149        assert!(serialized.get("meta").is_none());
150    }
151}