1use std::env;
4use std::net::{SocketAddr, UdpSocket};
5use std::sync::Arc;
6
7use serde::Serialize;
8
9use crate::error::{Error, Result};
10
11pub trait Client: Clone + std::fmt::Debug + Send + Sync {
13 fn send<S>(&self, data: &S) -> Result<()>
15 where
16 S: Serialize;
17}
18
19#[derive(Clone, Debug)]
21pub struct DaemonClient {
22 socket: Arc<UdpSocket>,
23}
24
25impl DaemonClient {
26 const HEADER: &'static [u8] = br#"{"format": "json", "version": 1}"#;
27 const DELIMITER: &'static [u8] = &[b'\n'];
28
29 pub fn new(addr: SocketAddr) -> Result<Self> {
32 let socket = Arc::new(UdpSocket::bind(&[([0, 0, 0, 0], 0).into()][..])?);
33 socket.set_nonblocking(true)?;
34 socket.connect(addr)?;
35 Ok(DaemonClient { socket })
36 }
37
38 pub fn from_lambda_env() -> Result<Self> {
46 let addr: SocketAddr = env::var("AWS_XRAY_DAEMON_ADDRESS")
47 .map_err(|_| Error::MissingEnvVar("AWS_XRAY_DAEMON_ADDRESS"))?
48 .parse::<SocketAddr>()
49 .map_err(|e| Error::BadConfig(format!("invalid X-Ray daemon address: {e}")))?;
50 DaemonClient::new(addr)
51 }
52
53 #[inline]
54 fn packet<S>(data: S) -> Result<Vec<u8>>
55 where
56 S: Serialize,
57 {
58 let bytes = serde_json::to_vec(&data)?;
59 Ok([Self::HEADER, Self::DELIMITER, &bytes].concat())
60 }
61}
62
63impl Client for DaemonClient {
64 fn send<S>(&self, data: &S) -> Result<()>
65 where
66 S: Serialize,
67 {
68 self.socket.send(&Self::packet(data)?)?;
69 Ok(())
70 }
71}
72
73#[derive(Clone, Debug)]
75pub enum InfallibleClient<C> {
76 Op(C),
78 Noop,
80}
81
82impl<C> InfallibleClient<C> {
83 pub fn new<E>(result: std::result::Result<C, E>) -> Self {
85 match result {
86 Ok(client) => Self::Op(client),
87 Err(_) => Self::Noop,
88 }
89 }
90}
91
92impl<C> Client for InfallibleClient<C>
93where
94 C: Client,
95{
96 fn send<S>(&self, data: &S) -> Result<()>
97 where
98 S: Serialize,
99 {
100 match self {
101 Self::Op(client) => client.send(data),
102 Self::Noop => Ok(()),
103 }
104 }
105}
106
107pub trait IntoInfallibleClient {
127 type Client: Client;
129
130 fn into_infallible(self) -> InfallibleClient<Self::Client>;
132}
133
134impl<C> IntoInfallibleClient for std::result::Result<C, Error>
135where
136 C: Client,
137{
138 type Client = C;
139
140 fn into_infallible(self) -> InfallibleClient<C> {
141 InfallibleClient::new(self)
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148 #[test]
149 fn client_prefixes_packets_with_header() {
150 assert_eq!(
151 DaemonClient::packet(serde_json::json!({
152 "foo": "bar"
153 }))
154 .unwrap(),
155 [
156 br#"{"format": "json", "version": 1}"# as &[u8],
157 &[b'\n'],
158 br#"{"foo":"bar"}"#,
159 ]
160 .concat()
161 )
162 }
163}