use serde::{Deserialize, Deserializer, Serialize};
use serde::de::DeserializeOwned;
use serde::de;
use serde_json;
use std::error;
use std::fmt;
use std::result;
use errors::*;
use super::id::*;
use super::status::*;
use super::Resource;
use super::Script;
resource! {
api_name "execution";
#[derive(Debug, Deserialize, Clone)]
pub struct Execution {
pub status: GenericStatus,
pub execution: Data,
}
}
#[derive(Debug, Deserialize, Clone)]
pub struct Data {
#[serde(default)]
pub outputs: Vec<Output>,
pub result: Option<serde_json::Value>,
#[serde(default, skip_serializing)]
_hidden: (),
}
impl Data {
pub fn get<D: DeserializeOwned>(&self, name: &str) -> Result<D> {
for output in &self.outputs {
if output.name == name {
return output.get();
}
}
Err(ErrorKind::CouldNotGetOutput(name.to_owned()).into())
}
}
#[derive(Debug, Default, Serialize)]
pub struct Args {
pub script: Option<Id<Script>>,
#[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(default, skip_serializing)]
_hidden: (),
}
impl Args {
pub fn set_script(&mut self, id: Id<Script>) {
self.script = Some(id);
}
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)?;
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 super::Args for Args {
type Resource = Execution;
}
#[derive(Debug, Clone)]
pub struct Output {
pub name: String,
pub value: Option<serde_json::Value>,
pub type_: Option<String>,
_hidden: (),
}
impl Output {
pub fn get<D: DeserializeOwned>(&self) -> Result<D> {
let mkerr = || ErrorKind::CouldNotGetOutput(self.name.clone());
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.chain_err(&mkerr)
} else {
let err: Error = ErrorKind::OutputNotAvailable.into();
Err(err).chain_err(&mkerr)
}
}
}
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 an array of three strings")
}
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,
_hidden: (),
})
}
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: name,
value: Some(value),
type_: if type_ == "" { None } else { Some(type_) },
_hidden: (),
})
}
}
deserializer.deserialize_i32(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");
}