Skip to content

Instantly share code, notes, and snippets.

@zeryx
Last active May 24, 2017 20:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeryx/edd4453a3fe665e87d9c55501b9fb585 to your computer and use it in GitHub Desktop.
Save zeryx/edd4453a3fe665e87d9c55501b9fb585 to your computer and use it in GitHub Desktop.
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