use std::collections::HashMap;
use std::fmt;
use std::panic::Location;
pub type CodexOk<T> = CodexOkRaw<T, HashMap<String, String>>;
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct CodexOkRaw<T, C = HashMap<String, String>> {
pub value: T,
#[cfg_attr(feature = "serde", serde(skip))]
pub location: &'static Location<'static>,
pub execution_meta: C,
}
impl<T, C> CodexOkRaw<T, C> {
#[track_caller]
pub fn new(value: T) -> Self
where
C: Default,
{
Self {
value,
location: Location::caller(),
execution_meta: C::default(),
}
}
}
impl<T, S: ::std::hash::BuildHasher> CodexOkRaw<T, HashMap<String, String, S>> {
#[must_use]
pub fn with_meta<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
self.execution_meta.insert(key.into(), value.into());
self
}
}
impl<T: fmt::Display, C> fmt::Display for CodexOkRaw<T, C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"SUCCESS: {} | Location: {}:{}",
self.value,
self.location.file(),
self.location.line()
)
}
}
#[derive(Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct ExecutionContext {
pub duration_ms: u64,
pub affected_rows: usize,
pub process_id: u32,
}
impl ExecutionContext {
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[inline]
#[must_use]
pub const fn with_duration(mut self, duration_ms: u64) -> Self {
self.duration_ms = duration_ms;
self
}
#[inline]
#[must_use]
pub const fn with_affected_rows(mut self, affected_rows: usize) -> Self {
self.affected_rows = affected_rows;
self
}
#[inline]
#[must_use]
pub const fn with_process_id(mut self, process_id: u32) -> Self {
self.process_id = process_id;
self
}
}
#[cfg(feature = "serde")]
pub fn log_codex_ok<T, C>(result: &CodexOkRaw<T, C>)
where
T: serde::Serialize,
C: serde::Serialize,
{
if let Ok(json) = serde_json::to_string(result) {
println!("{json}");
}
}
pub trait CodexOkWrap: Sized {
#[track_caller]
fn into_codex(self) -> CodexOkRaw<Self>;
}
impl<T> CodexOkWrap for T {
#[track_caller]
fn into_codex(self) -> CodexOkRaw<Self> {
CodexOkRaw::new(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_codex_ok_creation() {
let ok_result = CodexOk::new(42).with_meta("duration_ms", "10");
assert_eq!(ok_result.value, 42);
assert!(ok_result.location.file().ends_with("ok.rs"));
let res = ok_result.execution_meta.get("duration_ms");
assert_eq!(res.map(std::string::String::as_str), Some("10"));
}
}