antithesis_sdk/internal/
mod.rs

1use serde::Serialize;
2use std::io::Error;
3
4use noop_handler::NoOpHandler;
5
6#[cfg(feature = "full")]
7use rustc_version_runtime::version;
8
9#[cfg(feature = "full")]
10use voidstar_handler::VoidstarHandler;
11#[cfg(feature = "full")]
12use local_handler::LocalHandler;
13
14#[cfg(feature = "full")]
15use once_cell::sync::Lazy;
16
17
18mod noop_handler;
19#[cfg(feature = "full")]
20mod voidstar_handler;
21
22#[cfg(feature = "full")]
23mod local_handler;
24
25
26#[derive(Serialize, Debug)]
27struct AntithesisLanguageInfo {
28    name: &'static str,
29    version: String,
30}
31
32#[derive(Serialize, Debug)]
33struct AntithesisVersionInfo {
34    language: AntithesisLanguageInfo,
35    sdk_version: &'static str,
36    protocol_version: &'static str,
37}
38
39#[derive(Serialize, Debug)]
40struct AntithesisSDKInfo {
41    antithesis_sdk: AntithesisVersionInfo,
42}
43
44// Hardly ever changes, refers to the underlying JSON representation
45#[allow(dead_code)]
46const PROTOCOL_VERSION: &str = "1.1.0";
47
48// Tracks SDK releases
49#[allow(dead_code)]
50const SDK_VERSION: &str = env!("CARGO_PKG_VERSION");
51
52pub const LOCAL_OUTPUT: &str = "ANTITHESIS_SDK_LOCAL_OUTPUT";
53
54#[cfg(feature = "full")]
55fn get_handler() -> Box<dyn LibHandler + Sync + Send> {
56    match VoidstarHandler::try_load() {
57        Ok(handler) => Box::new(handler),
58        Err(_) => match LocalHandler::new() {
59            Some(h) => Box::new(h),
60            None => Box::new(NoOpHandler::new()),
61        },
62    }
63}
64
65#[cfg(not(feature = "full"))]
66#[allow(dead_code)]
67fn get_handler() -> Box<dyn LibHandler + Sync + Send> {
68    Box::new(NoOpHandler::new())
69}
70
71#[cfg(feature = "full")]
72pub(crate) static LIB_HANDLER: Lazy<Box<dyn LibHandler + Sync + Send>> = Lazy::new(|| {
73    let handler = get_handler();
74    let s = serde_json::to_string(&sdk_info()).unwrap_or("{}".to_owned());
75    let _ = handler.output(s.as_str());
76    handler
77});
78
79
80#[cfg(not(feature = "full"))]
81pub(crate) static LIB_HANDLER: NoOpHandler = NoOpHandler{};
82
83pub(crate) trait LibHandler {
84    fn output(&self, value: &str) -> Result<(), Error>;
85    fn random(&self) -> u64;
86}
87
88// Made public so it can be invoked from the antithesis_sdk::random module
89pub(crate) fn dispatch_random() -> u64 {
90    LIB_HANDLER.random()
91}
92
93// Ignore any and all errors - either the output is completed,
94// or it fails silently.
95//
96// For a Voidstar handler, there is no indication that something failed
97//
98// For a Local handler, either:
99// - Output was not requested (so not really an error)
100// - Output was requested and attempted, but an io::Error was detected
101// in this case the io::Error is silently ignored.
102//
103// It would be possible to distinguish between these two cases
104// and report detected io:Error's but there is no requirement
105// to implement this.
106//
107// Made public so it can be invoked from the antithesis_sdk::lifecycle
108// and antithesis_sdk::assert module
109pub fn dispatch_output<T: Serialize + ?Sized>(json_data: &T) {
110    let s = serde_json::to_string(json_data).unwrap_or("{}".to_owned());
111    let _ = LIB_HANDLER.output(s.as_str());
112}
113
114#[cfg(feature = "full")]
115fn sdk_info() -> AntithesisSDKInfo {
116    let language_data = AntithesisLanguageInfo {
117        name: "Rust",
118        version: version().to_string(),
119    };
120
121    let version_data = AntithesisVersionInfo {
122        language: language_data,
123        sdk_version: SDK_VERSION,
124        protocol_version: PROTOCOL_VERSION,
125    };
126
127    AntithesisSDKInfo {
128        antithesis_sdk: version_data,
129    }
130}