use chrono::{DateTime, Utc};
use serde::de;
use serde::de::DeserializeOwned;
use serde::ser::SerializeSeq;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json;
use std::error;
use std::fmt;
use std::result;
use super::Execution;
use crate::errors::*;
use crate::resource;
use crate::resource::id::*;
use crate::resource::Script;
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Args {
pub script: Option<Id<Script>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub inputs: Vec<(String, serde_json::Value)>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub outputs: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
#[serde(skip)]
_placeholder: (),
}
impl Args {
pub fn set_script(&mut self, id: Id<Script>) {
self.script = Some(id);
}
pub fn set_name<S>(&mut self, name: S)
where
S: Into<String>,
{
self.name = Some(name.into());
}
pub fn add_input<S, V>(&mut self, name: S, value: V) -> Result<()>
where
S: Into<String>,
V: Serialize,
{
let val = serde_json::value::to_value(value)?;
if val != serde_json::Value::Null {
self.inputs.push((name.into(), val));
}
Ok(())
}
pub fn add_output<S>(&mut self, name: S)
where
S: Into<String>,
{
self.outputs.push(name.into());
}
}
impl resource::Args for Args {
type Resource = Execution;
}
#[derive(Clone, Debug)]
pub struct Output {
pub name: String,
pub value: Option<serde_json::Value>,
pub type_: Option<String>,
_placeholder: (),
}
impl Output {
pub fn get<D: DeserializeOwned>(&self) -> Result<D> {
if let Some(ref value) = self.value {
let result: result::Result<D, serde_json::error::Error> =
serde_json::value::from_value(value.to_owned());
result.map_err(|e| Error::could_not_get_output(&self.name, e))
} else {
Err(Error::could_not_get_output(
&self.name,
Error::OutputNotAvailable,
))
}
}
}
impl<'de> Deserialize<'de> for Output {
fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct OutputVisitor;
impl<'de> de::Visitor<'de> for OutputVisitor {
type Value = Output;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "either a string or a [name, value, type] sequence")
}
fn visit_str<E>(self, v: &str) -> result::Result<Self::Value, E>
where
E: error::Error,
{
Ok(Output {
name: v.to_owned(),
value: None,
type_: None,
_placeholder: (),
})
}
fn visit_seq<V>(
self,
mut visitor: V,
) -> result::Result<Self::Value, V::Error>
where
V: de::SeqAccess<'de>,
{
use serde::de::Error;
let name = visitor
.next_element()?
.ok_or_else(|| V::Error::custom("no name field in output"))?;
let value = visitor
.next_element()?
.ok_or_else(|| V::Error::custom("no value field in output"))?;
let type_ = visitor
.next_element()?
.ok_or_else(|| V::Error::custom("no type field in output"))?;
Ok(Output {
name,
value: Some(value),
type_: if type_ == "" { None } else { Some(type_) },
_placeholder: (),
})
}
}
deserializer.deserialize_any(OutputVisitor)
}
}
#[test]
fn deserialize_output_with_only_a_name() {
let json = r#""name""#;
let output: Output = serde_json::from_str(&json).unwrap();
assert_eq!(output.name, "name");
assert!(output.value.is_none());
assert!(output.get::<bool>().is_err());
assert!(output.type_.is_none());
}
#[test]
fn deserialize_output_with_name_and_value_but_no_type() {
let json = r#"["name", null, ""]"#;
let output: Output = serde_json::from_str(&json).unwrap();
assert_eq!(output.name, "name");
assert_eq!(output.get::<Option<bool>>().unwrap(), None);
assert!(output.type_.is_none());
}
#[test]
fn deserialize_output_with_everything() {
use resource::evaluation::{ClassificationResult, Evaluation};
let json =
r#"["evaluation", "evaluation/50650d563c19202679000000", "evaluation"]"#;
let output: Output = serde_json::from_str(&json).unwrap();
assert_eq!(output.name, "evaluation");
let value: Id<Evaluation<ClassificationResult>> = output.get().unwrap();
assert_eq!(value.as_str(), "evaluation/50650d563c19202679000000");
assert_eq!(output.type_.unwrap(), "evaluation");
}
#[test]
fn deserialize_multiple_outputs() {
let json = r#"
[
["evaluation", null, ""],
["final-ensemble", null, ""],
["fields", ["label", "id"], "list"]
]
"#;
let outputs: Vec<Output> = serde_json::from_str(&json).unwrap();
assert_eq!(outputs.len(), 3);
}
impl Serialize for Output {
fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(3))?;
seq.serialize_element(&self.name)?;
seq.serialize_element(&self.value)?;
if let Some(ref type_) = self.type_ {
seq.serialize_element(type_)?;
} else {
seq.serialize_element("")?;
}
seq.end()
}
}
#[derive(
Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
)]
pub enum LogLevel {
#[serde(rename = "info")]
Info,
#[serde(rename = "warning")]
Warning,
#[serde(rename = "error")]
Error,
}
#[derive(Clone, Debug)]
pub struct LogEntry {
pub log_level: LogLevel,
pub timestamp: DateTime<Utc>,
pub source_index: u64,
pub line_number: u64,
pub message: String,
_placeholder: (),
}
impl<'de> Deserialize<'de> for LogEntry {
fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct LogEntryVisitor;
impl<'de> de::Visitor<'de> for LogEntryVisitor {
type Value = LogEntry;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "a list containing log entry information")
}
fn visit_seq<V>(
self,
mut visitor: V,
) -> result::Result<Self::Value, V::Error>
where
V: de::SeqAccess<'de>,
{
use serde::de::Error;
let log_level = visitor.next_element()?.ok_or_else(|| {
V::Error::custom("no log_level field in log entry")
})?;
let timestamp = visitor.next_element()?.ok_or_else(|| {
V::Error::custom("no timestamp field in log entry")
})?;
let source_index = visitor.next_element()?.ok_or_else(|| {
V::Error::custom("no source_index field in log entry")
})?;
let line_number = visitor.next_element()?.ok_or_else(|| {
V::Error::custom("no line_number field in log entry")
})?;
let message = visitor.next_element()?.ok_or_else(|| {
V::Error::custom("no message field in log entry")
})?;
Ok(LogEntry {
log_level,
timestamp,
source_index,
line_number,
message,
_placeholder: (),
})
}
}
deserializer.deserialize_seq(LogEntryVisitor)
}
}
impl Serialize for LogEntry {
fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(5))?;
seq.serialize_element(&self.log_level)?;
seq.serialize_element(&self.timestamp)?;
seq.serialize_element(&self.source_index)?;
seq.serialize_element(&self.line_number)?;
seq.serialize_element(&self.message)?;
seq.end()
}
}
#[test]
fn deserialize_serialize_log_entry() {
let json = r#"["info","2016-04-17T01:13:30.713Z",0,30,"creating model 1"]"#;
let entry: LogEntry = serde_json::from_str(json).unwrap();
assert_eq!(entry.log_level, LogLevel::Info);
assert_eq!(entry.source_index, 0);
assert_eq!(entry.line_number, 30);
assert_eq!(entry.message, "creating model 1");
let ser_json = serde_json::to_string(&entry).unwrap();
assert_eq!(ser_json, json);
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OutputResource {
pub id: String,
#[serde(default)]
pub variable: Option<String>,
pub last_update: i64,
pub progress: f64,
pub task: Option<String>,
pub state: String,
#[serde(skip)]
_placeholder: (),
}