use serde_json::{Map, Value};
use super::fields;
pub type Usage = std::collections::BTreeMap<String, u64>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Block {
Text(String),
Thinking(String),
ToolUse {
id: String,
name: String,
input: String,
},
Unknown(String),
}
impl Block {
pub fn label(&self) -> String {
match self {
Self::Text(_) => TEXT.to_owned(),
Self::Thinking(_) => THINKING.to_owned(),
Self::ToolUse { .. } => TOOL_USE.to_owned(),
Self::Unknown(word) => word.clone(),
}
}
}
const TEXT: &str = "text";
const THINKING: &str = "thinking";
const TOOL_USE: &str = "tool-use";
pub(crate) fn block(v: &Value) -> Result<Block, String> {
let o = v.as_object().ok_or("block: not an object")?;
Ok(match fields::text(o, "kind")?.as_str() {
TEXT => Block::Text(fields::text(o, TEXT)?),
THINKING => Block::Thinking(fields::text(o, TEXT)?),
TOOL_USE => Block::ToolUse {
id: fields::text(o, "id")?,
name: fields::text(o, "name")?,
input: fields::text(o, "input")?,
},
other => Block::Unknown(other.to_owned()),
})
}
pub(crate) fn usage(o: &Map<String, Value>) -> Result<Usage, String> {
o.get("usage")
.and_then(Value::as_object)
.ok_or_else(|| "missing or non-object field \"usage\"".to_owned())?
.iter()
.map(|(name, count)| {
count
.as_u64()
.map(|n| (name.clone(), n))
.ok_or_else(|| format!("usage {name:?}: not a count"))
})
.collect()
}
#[cfg(test)]
mod tests;