use std::collections::VecDeque; | |
use serde_json::Value; | |
use either::Either; | |
use common::low_level_parsing::{replace_json, search_json}; | |
use common::video_error::VideoError; | |
use std::ops::{Index, IndexMut}; | |
static BATCH_INPUT: &'static str = "$BATCH_INPUT"; | |
static SINGLE_INPUT: &'static str = "$SINGLE_INPUT"; | |
static BATCH_OUTPUT: &'static str = "$BATCH_OUTPUT"; | |
static SINGLE_OUTPUT: &'static str = "$SINGLE_OUTPUT"; | |
#[derive(Debug, Clone)] | |
pub struct AdvancedInput { | |
batch_single: String, | |
in_path: VecDeque<String>, | |
in_array_iter: Option<usize>, | |
out_path: VecDeque<String>, | |
out_array_iter: Option<usize>, | |
source: Value, | |
} | |
impl AdvancedInput { | |
pub fn new (batch_single: String, in_path: VecDeque<String>, in_array_iter: Option<usize>, | |
out_path: VecDeque<String>, out_array_iter: Option<usize>, source: Value) -> AdvancedInput { | |
AdvancedInput {batch_single: batch_single, in_path: in_path, | |
in_array_iter: in_array_iter, out_array_iter: out_array_iter, | |
out_path: out_path, source: source} | |
} | |
pub fn option(&self) -> &str {&self.batch_single} | |
pub fn in_path(&self) -> &VecDeque<String> {&self.in_path} | |
pub fn in_array_iter(&self) -> Option<usize> {self.in_array_iter} | |
pub fn out_path(&self) -> &VecDeque<String> {&self.out_path} | |
pub fn out_array_iter(&self) -> Option<usize> {self.out_array_iter} | |
pub fn source(&self) -> &Value {&self.source} | |
} | |
pub fn replace_variables(obj: &AdvancedInput, input: Either<&Vec<String>, &str>, output: Either<&Vec<String>, &str>) -> Result<Value, VideoError> { | |
let mut mutable: Value = obj.source().clone(); | |
let mut in_path = obj.in_path().clone(); | |
let mut out_path = obj.out_path().clone(); | |
//for input | |
replace_json(&mut mutable, &mut in_path, obj.in_array_iter(), input)?; | |
//for output | |
replace_json(&mut mutable, &mut out_path, obj.out_array_iter(), output)?; | |
Ok(mutable) | |
} | |
pub fn search_for_keywords(json: &Value) -> Result<AdvancedInput, VideoError> { | |
let mut batch_in_path = VecDeque::new(); | |
let mut batch_out_path = VecDeque::new(); | |
let mut single_in_path = VecDeque::new(); | |
let mut single_out_path = VecDeque::new(); | |
let (batch_in, batch_in_iter) = search_json(json, &mut batch_in_path, BATCH_INPUT)?; | |
let (batch_out, batch_out_iter) = search_json(json, &mut batch_out_path, BATCH_OUTPUT)?; | |
let (single_in, single_in_iter) = search_json(json, &mut single_in_path, SINGLE_INPUT)?; | |
let (single_out, single_out_iter) = search_json(json, &mut single_out_path, SINGLE_OUTPUT)?; | |
if batch_in && batch_out { | |
println!("json parsed as batch input."); | |
Ok(AdvancedInput::new("batch".to_string(), batch_in_path, batch_in_iter, batch_out_path, batch_out_iter, json.clone())) | |
} else if batch_in || batch_out { | |
Err(String::from("json parsing error:\nif batch selected both $BATCH_INPUT and $BATCH_OUTPUT must be defined.").into()) | |
} else if single_in && single_out { | |
println!("json parsed as single input."); | |
Ok(AdvancedInput::new("single".to_string(), single_in_path, single_in_iter, single_out_path, single_out_iter, json.clone())) | |
} else if single_in || single_out { | |
Err(String::from("json parsing error:\nif single selected both $SINGLE_INPUT and $SINGLE_OUTPUT must be defined.").into()) | |
} else { | |
Err(String::from("json parsing error:\nadvanced_input did not contain any keywords!").into()) | |
} | |
} | |
#[macro_use] extern crate serde_derive; | |
#[macro_use] extern crate serde_json; | |
use serde_json::Value; | |
use std::collections::VecDeque; | |
use serde_json::Value; | |
use common::extract_and_replace::{AdvancedInput, replace_variables, advanced_input_search} | |
//arbitrarily complicated input, some of our algorithms have inputs like this! | |
let input: Value = json!({ | |
"foo":"$SINGLE_INPUT", | |
"bar":{ | |
"someProperty":"someValue", | |
"anotherProperty":"anotherValue", | |
"theOutput":"$SINGLE_OUTPUT" | |
} | |
}); | |
//first we search for the important keywords, if found return an AdvancedInput struct | |
let algo_input: AdvancedInput = advanced_input_search(&input).unwrap(); | |
//now lets define some dummy url lists to replace our keywords with in the json template | |
let input_urls: Vec<String> = ["data://.session/some_file-001.png", "data://.session/some_file-002.png", "data://.session/some_file-003.png"] | |
let output_urls: Vec<String> = ["data://.session/output_file-001.png", "data://.session/output_file-002.png", "data://.session/output_file-003.png"] | |
//finally, lets replace the json keywords with our variables, simple right? | |
let output: Value = replace_variables(algo_input, Left(&input_url), Left(&output_url)).unwrap(); | |
// input = { | |
// "foo":"$BATCH_INPUT", | |
// "bar":{ | |
// "someProperty":"someValue", | |
// "anotherProperty":"anotherValue", | |
// "theOutput":"$BATCH_INPUT" | |
// } | |
// } | |
// output = { | |
// "foo":["data://.session/some_file-001.png", "data://.session/some_file-002.png", "data://.session/some_file-003.png"], | |
// "bar":{ | |
// "someProperty":"someValue", | |
// "anotherProperty":"anotherValue", | |
// "theOutput":["data://.session/output_file-001.png", "data://.session/output_file-002.png", "data://.session/output_file-003.png"] | |
// } | |
// } |
use std::collections::VecDeque; | |
use serde_json; | |
use serde_json::Value; | |
use common::video_error::VideoError; | |
use either::{Either, Left, Right}; | |
use std::ops::{Index, IndexMut}; | |
static BATCH_INPUT: &'static str = "$BATCH_INPUT"; | |
static SINGLE_INPUT: &'static str = "$SINGLE_INPUT"; | |
static BATCH_OUTPUT: &'static str = "$BATCH_OUTPUT"; | |
static SINGLE_OUTPUT: &'static str = "$SINGLE_OUTPUT"; | |
//depth first search of json blob, returns true if the keyword was found, false if it wasn't. | |
pub fn search_json(json: &Value, path: &mut VecDeque<String>, keyword: &str) -> Result<(bool, Option<usize>), VideoError> { | |
if json.is_object() { | |
let tree = json.as_object().unwrap(); | |
for (k, v) in tree { | |
path.push_back(k.to_string()); | |
match v { | |
&Value::String(ref text) if text == keyword => { | |
return Ok((true, None)) | |
} | |
&Value::Object(ref cursor) => { | |
let (found, iter) = try!(search_json(&v, path, keyword)); | |
if found { | |
return Ok((found, iter)) | |
} | |
} | |
&Value::Array(ref arr) => { | |
for i in 0..arr.len() { | |
let val = arr.index(i); | |
match val { | |
&Value::String(ref str) if str == keyword => { | |
return Ok((true, Some(i))) | |
} | |
_ => {} | |
} | |
} | |
} | |
_ => {} | |
} | |
path.pop_back(); | |
} | |
Ok((false, None)) | |
} else if json.is_array() { | |
let arr = json.as_array().unwrap(); | |
for i in 0..arr.len() { | |
let mut val = arr.index(i); | |
match val { | |
&Value::String(ref str) if str == keyword => { | |
return Ok((true, Some(i))) | |
} | |
_ => {} | |
} | |
} | |
return Ok((false, None)) | |
} | |
else if json.is_string() { | |
if json.as_str().unwrap() == keyword { | |
return Ok((true, None)) | |
} | |
else { | |
return Ok((false, None)) | |
} | |
} | |
else { | |
Err(format!("advanced input was neither a json object or an array.").into()) | |
} | |
} | |
//gets the cursor of the json tree into the requested scope | |
fn get_cursor<'a>(input: &'a mut Value, path_vec: &mut VecDeque<String>) -> Result< &'a mut Value, VideoError> { | |
if input.is_object() { | |
let path = path_vec.pop_front().ok_or(format!("did not exit properly, path contained a json object, not a final node of array or string."))?; | |
let b_tree_obj = input.as_object_mut().unwrap(); | |
let mut json = b_tree_obj.get_mut(&path).ok_or(format!("path traversal invalid, path not found."))?; | |
Ok(get_cursor(json, path_vec)?) | |
} else { | |
Ok(input) | |
} | |
} | |
//using the path variable, this traverses the mutable base Json tree to find the keyword containing key/value pair, once found it replaces the key/value pair with either an Array or String, depending on the data. | |
pub fn replace_json(base: &mut Value, paths: &mut VecDeque<String>, array_iter: Option<usize>, data: Either<&Vec<String>, &str>) -> Result< (), VideoError> { | |
//since for our usecase we'll never see obj -> array -> obj -> result, we can safely exit when it finds the key points to an array. | |
let cursor: &mut Value = get_cursor(base, paths)?; | |
//we if check instead of pattern match because we don't want to borrow cursor, for the string branch, since we need to replace the json, not the string. | |
if cursor.is_array() { | |
let mut arr: &mut Vec<Value> = cursor.as_array_mut().unwrap(); | |
let index = try!(array_iter.ok_or(format!("array iter must be passed if the final node is an array type."))); | |
match data { | |
Left(batch) => { | |
let mut formatted_array = Value::Array(batch.iter() | |
.map(|d| { Value::String(d.to_string()) }).collect::<Vec<Value>>()); | |
*arr.index_mut(index) = formatted_array; | |
} | |
Right(single) => { | |
let mut formatted_string = Value::String(single.to_string()); | |
*arr.index_mut(index) = formatted_string; | |
} | |
} | |
Ok(()) | |
} else if cursor.is_string() { | |
match data { | |
Left(array) => { | |
*cursor = Value::Array(array.iter() | |
.map(|d| { Value::String(d.to_string()) }) | |
.collect::<Vec<Value>>()); | |
} | |
Right(single) => { | |
*cursor = Value::String(single.to_string()); | |
} | |
} | |
Ok(()) | |
} else { Err(format!("cursor was not an array or a string.").into()) } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment