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