use crate::v4::{deserialize_source, CellMetadata};
use serde::Deserialize;
use serde_json::Value;
#[derive(Deserialize, Debug, Clone)]
#[serde(tag = "output_type")]
pub enum Output {
#[serde(rename = "stream")]
Stream {
#[serde(default)]
name: Option<String>,
#[serde(rename = "stream", default)]
stream: Option<String>,
#[serde(default, deserialize_with = "deserialize_source")]
text: Vec<String>,
},
#[serde(rename = "pyout")]
PyOut {
#[serde(default)]
prompt_number: Option<i32>,
#[serde(default)]
metadata: Value,
#[serde(flatten)]
extra_fields: serde_json::Map<String, Value>,
},
#[serde(rename = "display_data")]
DisplayData {
#[serde(default)]
metadata: Value,
#[serde(flatten)]
extra_fields: serde_json::Map<String, Value>,
},
#[serde(rename = "pyerr")]
PyErr {
#[serde(default)]
ename: Option<String>,
#[serde(default)]
evalue: Option<String>,
#[serde(default)]
traceback: Vec<String>,
},
}
#[derive(Deserialize, Debug)]
pub struct Notebook {
#[serde(default)]
pub metadata: Option<Value>,
pub nbformat: i32,
#[serde(default)]
pub nbformat_minor: Option<i32>,
#[serde(default)]
pub worksheets: Option<Vec<Worksheet>>,
}
#[derive(Deserialize, Debug)]
pub struct Worksheet {
#[serde(default)]
pub cells: Vec<Cell>,
#[serde(default)]
pub metadata: Option<Value>,
}
#[derive(Deserialize, Debug)]
#[serde(tag = "cell_type")]
pub enum Cell {
#[serde(rename = "heading")]
Heading {
level: i32,
metadata: CellMetadata,
#[serde(default, deserialize_with = "deserialize_source")]
source: Vec<String>,
},
#[serde(rename = "markdown")]
Markdown {
metadata: CellMetadata,
#[serde(default, deserialize_with = "deserialize_source")]
source: Vec<String>,
#[serde(default)]
attachments: Option<serde_json::Value>,
},
#[serde(rename = "code")]
Code {
metadata: CellMetadata,
#[serde(default)]
prompt_number: Option<i32>,
#[serde(default, deserialize_with = "deserialize_optional_source")]
input: Option<Vec<String>>,
#[serde(default)]
language: Option<String>,
#[serde(default)]
outputs: Vec<Output>,
},
#[serde(rename = "raw")]
Raw {
metadata: CellMetadata,
#[serde(default, deserialize_with = "deserialize_source")]
source: Vec<String>,
},
}
pub fn deserialize_optional_source<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
where
D: serde::Deserializer<'de>,
{
struct OptionalSourceVisitor;
impl<'de> serde::de::Visitor<'de> for OptionalSourceVisitor {
type Value = Option<Vec<String>>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a string, an array of strings, or null")
}
fn visit_none<E: serde::de::Error>(self) -> Result<Self::Value, E> {
Ok(None)
}
fn visit_unit<E: serde::de::Error>(self) -> Result<Self::Value, E> {
Ok(None)
}
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
Ok(Some(vec![v.to_string()]))
}
fn visit_string<E: serde::de::Error>(self, v: String) -> Result<Self::Value, E> {
Ok(Some(vec![v]))
}
fn visit_seq<A: serde::de::SeqAccess<'de>>(
self,
mut seq: A,
) -> Result<Self::Value, A::Error> {
let mut lines = Vec::new();
while let Some(line) = seq.next_element::<String>()? {
lines.push(line);
}
Ok(Some(lines))
}
}
deserializer.deserialize_any(OptionalSourceVisitor)
}
pub fn join_media_value(value: &Value) -> Option<String> {
match value {
Value::String(s) => Some(s.clone()),
Value::Array(arr) => Some(
arr.iter()
.filter_map(|v| v.as_str())
.collect::<Vec<_>>()
.join(""),
),
_ => None,
}
}