Last active
May 24, 2017 20:56
-
-
Save zeryx/edd4453a3fe665e87d9c55501b9fb585 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[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"] | |
// } | |
// } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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