1use serde::Deserialize;
6use serde_json::{Map, Value};
7
8use crate::client::Sandbox;
9use crate::error::SolariError;
10use crate::types::{CodeResultItem, RunCodeResult};
11
12#[derive(Default, Clone)]
14pub struct RunCodeOptions {
15 pub language: Option<String>,
17 pub context_id: Option<String>,
19}
20
21#[derive(Default, Deserialize)]
22struct RawRunCode {
23 #[serde(default)]
24 results: Option<Vec<CodeResultItem>>,
25 #[serde(default)]
26 error: Option<Value>,
27}
28
29pub(crate) fn build_run_code_result(v: Value) -> RunCodeResult {
32 let raw: RawRunCode = serde_json::from_value(v).unwrap_or_default();
33 let results = raw.results.unwrap_or_default();
34 let charts = results.iter().filter_map(|i| i.chart.clone()).collect();
35 RunCodeResult {
36 results,
37 error: raw.error,
38 charts,
39 }
40}
41
42pub struct Code<'a> {
44 pub(crate) sb: &'a Sandbox,
45}
46
47impl<'a> Code<'a> {
48 pub async fn run(&self, code: &str, opts: RunCodeOptions) -> Result<RunCodeResult, SolariError> {
50 self.sb.channel().connect().await?;
51 let mut p = Map::new();
52 p.insert("code".into(), Value::String(code.to_string()));
53 if let Some(lang) = &opts.language {
54 p.insert("language".into(), Value::String(lang.clone()));
55 }
56 if let Some(ctx) = &opts.context_id {
57 p.insert("contextId".into(), Value::String(ctx.clone()));
58 }
59 let raw = self.sb.channel().call("code.run", Value::Object(p)).await?;
60 Ok(build_run_code_result(raw))
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67 use crate::types::ChartType;
68
69 #[test]
70 fn flattens_result_charts_into_top_level() {
71 let reply = serde_json::json!({
72 "results": [
73 { "type": "stdout", "text": "hi" },
74 { "type": "result", "png": "AAAA",
75 "chart": { "type": "line", "title": "T",
76 "elements": [{"points": [[1,2]]}] } },
77 { "type": "result", "chart": { "type": "bar" } },
78 { "type": "result", "html": "<b/>" }
79 ],
80 "error": null
81 });
82 let out = build_run_code_result(reply);
83 assert_eq!(out.results.len(), 4);
84 assert_eq!(out.charts.len(), 2);
85 assert_eq!(out.charts[0].chart_type, ChartType::Line);
86 assert_eq!(out.charts[0].title.as_deref(), Some("T"));
87 assert!(out.charts[0].elements.is_some());
89 assert_eq!(out.charts[1].chart_type, ChartType::Bar);
90 }
91
92 #[test]
93 fn unknown_chart_type_does_not_fail() {
94 let reply = serde_json::json!({
95 "results": [ { "type": "result", "chart": { "type": "sankey" } } ]
96 });
97 let out = build_run_code_result(reply);
98 assert_eq!(out.charts.len(), 1);
99 assert_eq!(out.charts[0].chart_type, ChartType::Unknown);
100 }
101}