#![forbid(unsafe_code)]
use kcode_k1_chat_boxes::{BoxId, ChatBox, ToolCallId};
use kcode_k1_chat_web_code_output::{DocumentPart, WebCodeOutput, create, open};
use kcode_k1_web_code_document::CodeDocument;
use std::fmt::{Display, Formatter};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SourceLayout {
originating_call: BoxId,
tool_call_id: ToolCallId,
code_message_count: u64,
}
impl SourceLayout {
pub const fn originating_call(self) -> BoxId {
self.originating_call
}
pub const fn tool_call_id(self) -> ToolCallId {
self.tool_call_id
}
pub const fn code_message_count(self) -> u64 {
self.code_message_count
}
}
pub type OpenLayout = SourceLayout;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SourcePresentation {
pub output: WebCodeOutput,
pub layout: SourceLayout,
}
pub type OpenPresentation = SourcePresentation;
pub fn present_create(
originating_call: BoxId,
tool_call_id: ToolCallId,
document: &CodeDocument,
version: &str,
) -> SourcePresentation {
present(originating_call, tool_call_id, document, version, true)
}
pub fn present_open(
originating_call: BoxId,
tool_call_id: ToolCallId,
document: &CodeDocument,
version: &str,
) -> SourcePresentation {
present(originating_call, tool_call_id, document, version, false)
}
fn present(
originating_call: BoxId,
tool_call_id: ToolCallId,
document: &CodeDocument,
version: &str,
creating: bool,
) -> SourcePresentation {
let ranges = document.chunk_ranges();
let code_message_count = u64::try_from(ranges.len()).expect("code chunk count fits u64");
let mut messages = Vec::with_capacity(ranges.len() + 1);
messages.push(text(document.documentation()));
messages.extend(
ranges
.iter()
.map(|range| text(&document.code()[range.clone()])),
);
let name = document.family().logical_name();
let result = if creating {
Ok(format!("Created {version}."))
} else {
Ok(format!("Opened {version}."))
};
let output = if creating {
create(
tool_call_id,
messages,
result,
name,
version,
document.language(),
code_message_count,
)
} else {
open(
tool_call_id,
messages,
result,
name,
version,
document.language(),
code_message_count,
)
};
SourcePresentation {
output,
layout: SourceLayout {
originating_call,
tool_call_id,
code_message_count,
},
}
}
fn text(bytes: &[u8]) -> String {
std::str::from_utf8(bytes)
.expect("CodeDocument retains UTF-8")
.to_owned()
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TargetRejection {
HistoryAbsent,
HistoryDuplicate,
MalformedPayload,
DifferentOpen,
MessageOutOfLayout,
NotSourceMessage,
}
impl TargetRejection {
pub const fn code(self) -> &'static str {
match self {
Self::HistoryAbsent => "history_absent",
Self::HistoryDuplicate => "history_duplicate",
Self::MalformedPayload => "malformed_payload",
Self::DifferentOpen => "different_open",
Self::MessageOutOfLayout => "message_out_of_layout",
Self::NotSourceMessage => "not_source_message",
}
}
}
impl Display for TargetRejection {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
formatter.write_str(match self {
Self::HistoryAbsent => "target box is absent from history",
Self::HistoryDuplicate => "target box ID occurs more than once in history",
Self::MalformedPayload => "target has malformed tool metadata",
Self::DifferentOpen => "target belongs to a different source Open",
Self::MessageOutOfLayout => "target message index is outside the source layout",
Self::NotSourceMessage => "target is not an editable source message",
})
}
}
impl std::error::Error for TargetRejection {}
pub fn classify_original_target(
history: &[ChatBox],
target_id: BoxId,
layout: SourceLayout,
) -> Result<DocumentPart, TargetRejection> {
let mut matching = history.iter().filter(|value| value.id() == target_id);
let target = matching.next().ok_or(TargetRejection::HistoryAbsent)?;
if matching.next().is_some() {
return Err(TargetRejection::HistoryDuplicate);
}
let message = target
.tool_message_metadata()
.map_err(|_| TargetRejection::MalformedPayload)?
.ok_or(TargetRejection::NotSourceMessage)?;
if message.tool_call_id != layout.tool_call_id
|| message.originating_call != layout.originating_call
{
return Err(TargetRejection::DifferentOpen);
}
match message.message_index {
1 => Ok(DocumentPart::Documentation),
index if (2..=layout.code_message_count + 1).contains(&index) => Ok(DocumentPart::Code),
_ => Err(TargetRejection::MessageOutOfLayout),
}
}