use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;
use crate::common;
use crate::harness::{campfire, claude_code, codex, cursor, opencode, pi};
use crate::transcript::{Codec, Common, HarnessId, TextCodec, Transcript};
#[wasm_bindgen]
pub fn convert(input: &str, from: &str, to: &str) -> Result<String, JsError> {
let from = parse_harness(from)?;
let to = parse_harness(to)?;
let common = parse_to_common(from, input).map_err(js)?;
render_from_common(to, &common).map_err(js)
}
#[wasm_bindgen(js_name = toCommon)]
pub fn to_common(input: &str, from: &str) -> Result<String, JsError> {
let from = parse_harness(from)?;
let common = parse_to_common(from, input).map_err(js)?;
serde_json::to_string(&CommonJson {
meta: common.meta,
messages: common.body,
})
.map_err(js)
}
#[wasm_bindgen(js_name = fromCommon)]
pub fn from_common(common_json: &str, to: &str) -> Result<String, JsError> {
let to = parse_harness(to)?;
let parsed: CommonJson = serde_json::from_str(common_json).map_err(js)?;
let common = Transcript::new(parsed.meta, parsed.messages);
render_from_common(to, &common).map_err(js)
}
#[wasm_bindgen]
pub fn harnesses() -> Vec<String> {
HarnessId::ALL
.iter()
.map(|h| h.as_str().to_string())
.collect()
}
#[derive(Serialize, Deserialize)]
struct CommonJson {
meta: common::Meta,
messages: Vec<common::Message>,
}
fn parse_to_common(harness: HarnessId, text: &str) -> crate::Result<Transcript<Common>> {
fn go<H: TextCodec + Codec>(text: &str) -> crate::Result<Transcript<Common>> {
H::to_common(&H::from_text(text)?)
}
match harness {
HarnessId::ClaudeCode => go::<claude_code::ClaudeCode>(text),
HarnessId::Codex => go::<codex::Codex>(text),
HarnessId::OpenCode => go::<opencode::OpenCode>(text),
HarnessId::Pi => go::<pi::Pi>(text),
HarnessId::Campfire => go::<campfire::Campfire>(text),
HarnessId::Cursor => go::<cursor::Cursor>(text),
}
}
fn render_from_common(harness: HarnessId, common: &Transcript<Common>) -> crate::Result<String> {
fn go<H: TextCodec + Codec>(common: &Transcript<Common>) -> crate::Result<String> {
H::to_text(&H::from_common(common)?)
}
match harness {
HarnessId::ClaudeCode => go::<claude_code::ClaudeCode>(common),
HarnessId::Codex => go::<codex::Codex>(common),
HarnessId::OpenCode => go::<opencode::OpenCode>(common),
HarnessId::Pi => go::<pi::Pi>(common),
HarnessId::Campfire => go::<campfire::Campfire>(common),
HarnessId::Cursor => go::<cursor::Cursor>(common),
}
}
fn parse_harness(name: &str) -> Result<HarnessId, JsError> {
name.parse().map_err(js)
}
fn js<E: std::fmt::Display>(e: E) -> JsError {
JsError::new(&e.to_string())
}