1#![forbid(unsafe_code)]
2
3use kcode_k1_chat_boxes::ToolCallId;
4pub use kcode_k1_web_code_ktool_protocol::{
5 Language, WEB_CODE_CHECK_METADATA_TYPE, WEB_CODE_DOCS_METADATA_TYPE,
6 WEB_CODE_OPEN_METADATA_TYPE, WEB_CODE_OVERWRITE_METADATA_TYPE, WEB_CODE_PUBLISH_METADATA_TYPE,
7};
8use serde::Serialize;
9
10#[derive(Clone, Debug, Eq, PartialEq)]
11pub struct WebCodeOutput {
12 pub tool_call_id: ToolCallId,
13 pub messages: Vec<String>,
14 pub result: Result<String, String>,
15 pub metadata_type: String,
16 pub metadata_contents: String,
17}
18
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
20pub enum PublishCheck {
21 ReusedSameSession,
22 Fresh,
23}
24
25impl PublishCheck {
26 pub const fn as_str(self) -> &'static str {
27 match self {
28 Self::ReusedSameSession => "reused_same_session",
29 Self::Fresh => "fresh",
30 }
31 }
32}
33
34#[derive(Clone, Copy, Debug, Eq, PartialEq)]
35pub enum PublishStatus {
36 Published,
37 Idempotent,
38 Conflict,
39}
40
41impl PublishStatus {
42 pub const fn as_str(self) -> &'static str {
43 match self {
44 Self::Published => "published",
45 Self::Idempotent => "idempotent",
46 Self::Conflict => "conflict",
47 }
48 }
49}
50
51#[derive(Clone, Copy, Debug, Eq, PartialEq)]
52pub enum DocumentPart {
53 Documentation,
54 Code,
55}
56
57impl DocumentPart {
58 pub const fn as_str(self) -> &'static str {
59 match self {
60 Self::Documentation => "documentation",
61 Self::Code => "code",
62 }
63 }
64}
65
66#[derive(Clone, Copy, Debug, Eq, PartialEq)]
67pub enum Operation {
68 Docs,
69 Open,
70 Overwrite,
71 Check,
72 Publish,
73}
74
75impl Operation {
76 pub const fn metadata_type(self) -> &'static str {
77 match self {
78 Self::Docs => WEB_CODE_DOCS_METADATA_TYPE,
79 Self::Open => WEB_CODE_OPEN_METADATA_TYPE,
80 Self::Overwrite => WEB_CODE_OVERWRITE_METADATA_TYPE,
81 Self::Check => WEB_CODE_CHECK_METADATA_TYPE,
82 Self::Publish => WEB_CODE_PUBLISH_METADATA_TYPE,
83 }
84 }
85}
86
87#[derive(Serialize)]
88struct DocsMetadata<'a> {
89 status: &'static str,
90 authority: &'a str,
91 name: &'a str,
92 version: &'a str,
93}
94
95#[derive(Serialize)]
96struct OpenMetadata<'a> {
97 status: &'static str,
98 authority: &'a str,
99 name: &'a str,
100 version: &'a str,
101 language: &'static str,
102 documentation_message_index: u64,
103 code_message_start: u64,
104 code_message_count: u64,
105}
106
107#[derive(Serialize)]
108struct OverwriteMetadata<'a> {
109 status: &'static str,
110 box_id: &'a str,
111 part: &'static str,
112}
113
114#[derive(Serialize)]
115struct CheckSuccessMetadata {
116 status: &'static str,
117 fresh: bool,
118}
119
120#[derive(Serialize)]
121struct CheckFailedMetadata<'a> {
122 status: &'static str,
123 diagnostics: &'a str,
124}
125
126#[derive(Serialize)]
127struct PublishMetadata<'a> {
128 status: &'static str,
129 source_object: &'a str,
130 winning: &'a str,
131 submitted: Option<&'a str>,
132 check: &'static str,
133}
134
135#[derive(Serialize)]
136struct ErrorMetadata<'a> {
137 status: &'static str,
138 code: &'a str,
139 source_object: Option<&'a str>,
140}
141
142pub fn docs(
143 tool_call_id: ToolCallId,
144 messages: Vec<String>,
145 result: Result<String, String>,
146 authority: &str,
147 name: &str,
148 version: &str,
149) -> WebCodeOutput {
150 build(
151 tool_call_id,
152 messages,
153 result,
154 WEB_CODE_DOCS_METADATA_TYPE,
155 &DocsMetadata {
156 status: "found",
157 authority,
158 name,
159 version,
160 },
161 )
162}
163
164#[allow(clippy::too_many_arguments)]
165pub fn open(
166 tool_call_id: ToolCallId,
167 messages: Vec<String>,
168 result: Result<String, String>,
169 authority: &str,
170 name: &str,
171 version: &str,
172 language: Language,
173 code_message_count: u64,
174) -> WebCodeOutput {
175 build(
176 tool_call_id,
177 messages,
178 result,
179 WEB_CODE_OPEN_METADATA_TYPE,
180 &OpenMetadata {
181 status: "opened",
182 authority,
183 name,
184 version,
185 language: language.as_str(),
186 documentation_message_index: 1,
187 code_message_start: 2,
188 code_message_count,
189 },
190 )
191}
192
193pub fn overwrite(
194 tool_call_id: ToolCallId,
195 messages: Vec<String>,
196 result: Result<String, String>,
197 box_id: &str,
198 part: DocumentPart,
199) -> WebCodeOutput {
200 build(
201 tool_call_id,
202 messages,
203 result,
204 WEB_CODE_OVERWRITE_METADATA_TYPE,
205 &OverwriteMetadata {
206 status: "overwritten",
207 box_id,
208 part: part.as_str(),
209 },
210 )
211}
212
213pub fn check_success(
214 tool_call_id: ToolCallId,
215 messages: Vec<String>,
216 result: Result<String, String>,
217) -> WebCodeOutput {
218 build(
219 tool_call_id,
220 messages,
221 result,
222 WEB_CODE_CHECK_METADATA_TYPE,
223 &CheckSuccessMetadata {
224 status: "success",
225 fresh: true,
226 },
227 )
228}
229
230pub fn check_failed(
231 tool_call_id: ToolCallId,
232 messages: Vec<String>,
233 result: Result<String, String>,
234 diagnostics: &str,
235) -> WebCodeOutput {
236 build(
237 tool_call_id,
238 messages,
239 result,
240 WEB_CODE_CHECK_METADATA_TYPE,
241 &CheckFailedMetadata {
242 status: "failed",
243 diagnostics,
244 },
245 )
246}
247
248#[allow(clippy::too_many_arguments)]
249pub fn publish(
250 tool_call_id: ToolCallId,
251 messages: Vec<String>,
252 result: Result<String, String>,
253 source_object: &str,
254 winning: &str,
255 submitted: Option<&str>,
256 check: PublishCheck,
257 status: PublishStatus,
258) -> WebCodeOutput {
259 build(
260 tool_call_id,
261 messages,
262 result,
263 WEB_CODE_PUBLISH_METADATA_TYPE,
264 &PublishMetadata {
265 status: status.as_str(),
266 source_object,
267 winning,
268 submitted,
269 check: check.as_str(),
270 },
271 )
272}
273
274pub fn error(
275 tool_call_id: ToolCallId,
276 messages: Vec<String>,
277 result: Result<String, String>,
278 operation: Operation,
279 code: &str,
280 source_object: Option<&str>,
281) -> WebCodeOutput {
282 build(
283 tool_call_id,
284 messages,
285 result,
286 operation.metadata_type(),
287 &ErrorMetadata {
288 status: "error",
289 code,
290 source_object,
291 },
292 )
293}
294
295fn build(
296 tool_call_id: ToolCallId,
297 messages: Vec<String>,
298 result: Result<String, String>,
299 metadata_type: &str,
300 metadata: &impl Serialize,
301) -> WebCodeOutput {
302 WebCodeOutput {
303 tool_call_id,
304 messages,
305 result,
306 metadata_type: metadata_type.to_owned(),
307 metadata_contents: serde_json::to_string(metadata)
308 .expect("fixed Web-code metadata is serializable"),
309 }
310}