pub mod claude_code;
pub mod codex;
use std::collections::BTreeMap;
use serde::Deserialize;
pub type ExtraFields = BTreeMap<String, serde_json::Value>;
#[derive(Debug, Clone, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentBlock {
Text {
#[serde(default)]
text: String,
},
Thinking {
#[serde(default)]
thinking: String,
},
RedactedThinking {
#[serde(default)]
data: Option<String>,
},
ToolUse {
id: String,
name: String,
#[serde(default)]
input: serde_json::Value,
},
ToolResult {
#[serde(default)]
tool_use_id: Option<String>,
#[serde(default)]
content: serde_json::Value,
},
Image {
#[serde(default)]
source: serde_json::Value,
},
InputText {
#[serde(default)]
text: String,
},
OutputText {
#[serde(default)]
text: String,
},
InputImage {
#[serde(flatten)]
extra: ExtraFields,
},
Fallback {
#[serde(flatten)]
extra: ExtraFields,
},
#[serde(other)]
Unknown,
}
impl ContentBlock {
pub fn tag(&self) -> Option<&'static str> {
Some(match self {
ContentBlock::Text { .. } => "text",
ContentBlock::Thinking { .. } => "thinking",
ContentBlock::RedactedThinking { .. } => "redacted_thinking",
ContentBlock::ToolUse { .. } => "tool_use",
ContentBlock::ToolResult { .. } => "tool_result",
ContentBlock::Image { .. } => "image",
ContentBlock::InputText { .. } => "input_text",
ContentBlock::OutputText { .. } => "output_text",
ContentBlock::InputImage { .. } => "input_image",
ContentBlock::Fallback { .. } => "fallback",
ContentBlock::Unknown => return None,
})
}
pub fn as_text(&self) -> Option<&str> {
match self {
ContentBlock::Text { text }
| ContentBlock::InputText { text }
| ContentBlock::OutputText { text } => Some(text),
_ => None,
}
}
}
pub fn raw_block_tag(v: &serde_json::Value) -> Option<String> {
v.get("type")
.and_then(serde_json::Value::as_str)
.map(str::to_string)
}