1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
use serde_json::{Map, Value}; use std::path::PathBuf; use thiserror::Error; use valico::json_schema::Scope; #[derive(Error, PartialEq, Debug)] pub enum Error { #[error("The rendered json is invalid")] Invalid, } pub type Result<T> = std::result::Result<T, Error>; #[derive(Default)] pub struct Filter { pub only: Vec<PathBuf>, pub except: Vec<PathBuf>, } impl Filter { fn pass(&self, path: &PathBuf) -> bool { let allow = self.only.iter().any(|allow| path.starts_with(allow)); let disallow = self .except .iter() .any(|disallow| path.starts_with(disallow)); (allow || self.only.is_empty()) && !disallow } } pub fn find(json: &Value, filter: &Filter) -> Result<Vec<Value>> { let mut objects = vec![]; let mut walker: Vec<Box<dyn Iterator<Item = (PathBuf, &Value)>>> = vec![Box::new(vec![(PathBuf::from("/"), json)].into_iter())]; while let Some(curr) = walker.last_mut() { let (base, json) = match curr.next() { Some(val) => val, None => { walker.pop(); continue; } }; if is_object(json) { if filter.pass(&base) { objects.push(json.to_owned()); } } else { match json { Value::Object(map) => { walker.push(Box::new(map.into_iter().map(move |(k, v)| { let mut path = base.clone(); path.push(k); (path, v) }))); } _ => return Err(Error::Invalid), } } } Ok(objects) } pub fn glue(obj: &[Value]) -> Value { if obj.len() == 1 { obj[0].to_owned() } else { let object = { let mut map = Map::<String, Value>::new(); map.insert( String::from("apiVersion"), Value::String(String::from("v1")), ); map.insert(String::from("kind"), Value::String(String::from("List"))); map.insert(String::from("items"), Value::Array(obj.to_owned())); map }; Value::Object(object) } } const K8S_OBJECT_SCHEMA: &str = r#"{ "$schema": "http://json-schema.org/schema#", "type": "object", "additionalProperties": true, "required": ["kind", "apiVersion"], "properties": { "kind": { "type": "string" }, "apiVersion": { "type": "string" } } }"#; fn is_object(obj: &Value) -> bool { let schema = serde_json::from_str(K8S_OBJECT_SCHEMA).unwrap(); let mut scope = Scope::new(); let validator = scope.compile_and_return(schema, false).unwrap(); validator.validate(&obj).is_strictly_valid() }