TFHE7SKK7K6UKJM3ATI2OU3QWX4YVAHHIFAAVLNIMA6EUZGZJWFAC
RQHPHZ6GVNUXCI4ZMV6UBGRGJZJHIAQVGCBUAUMMDNKRJJW76TJAC
FGD37V6XCUCI6IJGJP4SU4EFNEE7F3GQVBYJNK4XT2AYY34OCUTQC
GJVR74FXYXUF6APQ2RJJ27BE5ZG6JCN6OREHHPCV3TP6ZMNNVCAAC
4MK5VE34SUDTEN2C2VVFBEONHGFY3P3EDTEK2WPU7RH5IWDAT3DQC
DTAFS7PQQ6X2REJYR6EDKEM7TOKGIAX2YHIRRU354AOAFR2B3VIQC
VWUEKTF47O72TML5LWVFTOUI6P52AB5KW7FHMSSUGMYJWYAVPLFAC
A2SYW5RCLJO6WXUGQ7DLQA76DENJE2I5TLTUG6MXEI27ADPG2FVAC
SCCMQCSX6BA6DQKARGW25PVWQPU3MZNZS4RNZ53FXYFCKMI24RGQC
6WWZUDNYHVYEQJHHJCUBMOZME2LG4SATAXSADBECKPBUGR3QLYTAC
FWDHYYDCRS27NN2LVAP3VBCAFJAQ32X6WF4KRRD5ZBXAKZPX6TFQC
HBKDTCY3SEIVJQACNASSDPRXNABOKIJ55H2EAMBWJ6Z5HBCT5QAAC
use std::path::{Path, PathBuf};
use std::collections::HashMap;
use serde_yaml::{self, Value, Mapping};
use url::Url;
mod transformation;
use transformation::*;
#[serde(try_from = "RawKarbonfile")]
pub struct Karbonfile {
pub resources: Vec<Resource>,
pub generators: Vec<Generator>,
pub transformations: Vec<Transformation>,
}
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
#[serde(default)]
#[serde(default)]
#[serde(default)]
}
impl TryFrom<RawKarbonfile> for Karbonfile {
type Error = &'static str;
fn try_from(t: RawKarbonfile) -> Result<Self, &'static str> {
Ok(Self {
api_version: t.api_version,
kind: t.kind,
generators: t.generators,
transformations: t.transformations,
})
}
resources: t
.resources
.into_keys()
.collect(),
pub transformations: Vec<Transformation>,
pub generators: Vec<Generator>,
pub resources: HashMap<Resource, ()>,
struct RawKarbonfile {
pub api_version: ApiVersion,
pub kind: Kind,
#[derive(Deserialize, Serialize, Clone, Debug)]
}
pub enum Generator {
ConfigMap,
}
pub struct Variant {
// Original resource file Url.
pub url: Option<Url>,
// Parsed content of the original resource file.
pub original: Option<Value>,
}
original: None,
}
}
pub fn apply_transformations(&mut self) {
if let Some(mut result) = self.original.to_owned() {
}
}
}
}
self.result = Some(result);
});
self.transformations
.iter()
.flatten()
// Ignore the Karbonfile path.
.map(|(_, ts)| ts)
// Flatten all the transformations of all the
// Karbonfiles into a single Vec.
.flatten()
.for_each(|t| {
if matches_filter(&result, t.filter.as_ref()) {
t_add(&mut result, t.add.as_ref());
t_replace(&mut result, t.replace.as_ref());
t_prefix(&mut result, t.prefix.as_ref());
t_suffix(&mut result, t.suffix.as_ref());
result: None,
transformations: None,
impl Variant {
pub fn new() -> Variant {
Variant {
url: None,
// Resulting variant after all transformations
pub result: Option<Value>,
// Chain of transformations to apply with the Url of
// the Karbonfile they originate from.
pub transformations: Option<Vec<(Url, Vec<Transformation>)>>,
#[derive(Deserialize, Serialize, Clone, Debug)]
#[derive(Deserialize, Serialize, Clone, Debug)]
pub type ApiVersion = String;
pub type Kind = String;
pub type Resource = String;
pub api_version: ApiVersion,
pub kind: Kind,
#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use serde_yaml::Value;
use crate::json_pointer;
#[serde(deny_unknown_fields)]
pub struct Transformation {
pub filter: Option<Filter>,
pub add: Option<TransAdd>,
pub replace: Option<TransReplace>,
pub remove: Option<TransRemove>,
pub prefix: Option<TransPrefix>,
pub suffix: Option<TransSuffix>,
}
pub type Filter = HashMap<String, Value>;
pub type TransAdd = HashMap<String, Value>;
pub type TransReplace = HashMap<String, Value>;
pub type TransRemove = HashMap<String, ()>;
pub type TransPrefix = HashMap<String, String>;
pub type TransSuffix = HashMap<String, String>;
p.push_str(current);
*r = serde_yaml::from_str(&p).unwrap();
}
}
}
}
pub fn t_suffix(result: &mut Value, t: Option<&TransSuffix>) {
if let Some(map) = t {
for (ptr, suffix) in map {
if let Some(r) = json_pointer::get_mut(result, ptr) {
let mut current = r.as_str()
.expect(&format!("Failed to suffix. Field {ptr} doesn't contain a string"))
.to_string();
current.push_str(suffix);
*r = serde_yaml::from_str(¤t).unwrap();
}
}
}
}
pub fn matches_filter(doc: &Value, filter: Option<&Filter>) -> bool {
if let Some(f) = filter {
// Check each field from the filter and return
// false as soon as one doesn't match
for (field, value) in f.iter() {
if json_pointer::get(doc, field) != Some(value) {
return false;
}
}
}
// If all fields match or if no fields were provided
// keep this transformation in the list to apply.
true
}
pub fn t_add(result: &mut Value, t: Option<&TransAdd>) {
if let Some(map) = t {
for (ptr, value) in map {
json_pointer::insert(result, ptr, value);
}
}
}
pub fn t_replace(result: &mut Value, t: Option<&TransReplace>) {
if let Some(map) = t {
for (ptr, new_value) in map {
if let Some(r) = json_pointer::get_mut(result, ptr) {
*r = new_value.to_owned();
}
}
}
}
pub fn t_prefix(result: &mut Value, t: Option<&TransPrefix>) {
if let Some(map) = t {
for (ptr, prefix) in map {
let mut p = prefix.to_owned();
if let Some(r) = json_pointer::get_mut(result, ptr) {
let current = r.as_str()
.expect(&format!("Failed to prefix. Field {ptr} doesn't contain a string"));
#[derive(Deserialize, Serialize, Clone, Debug)]
use serde::{Deserialize, Serialize};
}
}
.split('/')
.skip(1)
.map(|x| x.replace("~1", "/").replace("~0", "~"))
.try_fold(doc, |target, token| {
match target {
Value::Mapping(map) => {
let t: Value = serde_yaml::from_str(&token).unwrap();
}
Value::Sequence(seq) => {
let t: usize = token.parse().unwrap();
}
}
})
}
//pub fn update<'a>(doc: &'a mut Yaml, pointer: &'a str) -> Option<&'a mut Yaml> {
// // Taken from serde JSON
// if pointer.is_empty() {
// return Some(doc);
// }
// if !pointer.starts_with('/') {
// return None;
// }
// pointer
// .split('/')
// .skip(1)
// .map(|x| x.replace("~1", "/").replace("~0", "~"))
// .try_fold(doc, |target, token| {
// match target {
// Yaml::Hash(map) => {
// let t: Yaml = Yaml::from_str(&token).unwrap();
// map.get_mut(&t)
// }
// Yaml::Sequence(seq) => {
// let t: usize = token.parse().unwrap();
// seq.get_mut(t)
// }
// _ => None,
// }
// })
//}
//
//
//pub fn upsert<'a>(doc: &'a mut Yaml, pointer: &'a str) -> Option<&'a mut Yaml> {
// // Taken from serde JSON
// if pointer.is_empty() {
// return Some(doc);
// }
// if !pointer.starts_with('/') {
// return None;
// }
// pointer
// .split('/')
// .skip(1)
// .map(|x| x.replace("~1", "/").replace("~0", "~"))
// .try_fold(doc, |target, token| {
// match target {
// Yaml::Hash(map) => {
// let t: Yaml = Yaml::from_str(&token).unwrap();
//
// if !map.contains_key(&t) {
// let next = Yaml::Hash();
//
// map.insert(t.to_owned(), Yaml::Hash(next));
// }
//
// map.get_mut(&t)
// }
// Yaml::Sequence(seq) => {
// let t: usize = token.parse().unwrap();
// seq.get_mut(t)
// }
// _ => None,
// }
// })
//}
let mut target_acc = ptr
.split('/')
.skip(1)
.map(|x| x.replace("~1", "/").replace("~0", "~"))
.try_fold(doc, |target, token| {
match target {
Value::Mapping(map) => {
let t: Value = serde_yaml::from_str(&token).unwrap();
map.get_mut(&t)
}
Value::Sequence(seq) => {
let t: usize = token.parse().unwrap();
seq.get_mut(t)
}
_ => {
None
}
}
});
let mut addition_acc: Value;
ptr
.windows(2)
.for_each(|token| {
if let Ok(n) = token.parse::<usize>() {
} else if let Ok(s) = token.parse::<str>() {
if s == "-" {
} else {
}
}
});
}
pub fn insert<'a>(doc: &'a mut Value, ptr: &'a str, value: &'a Value) {
if ptr.is_empty() {
return;
}
if !ptr.starts_with('/') {
return;
}
seq.get_mut(t)
}
_ => {
None
map.get_mut(&t)
ptr
if !ptr.starts_with('/') {
panic!("{} is not a valid pointer", &ptr);
}
if ptr.is_empty() {
panic!("Point is empty");
ptr
.split('/')
.skip(1)
.map(|x| x.replace("~1", "/").replace("~0", "~"))
.try_fold(doc, |target, token| {
match target {
Value::Mapping(map) => {
let t: Value = serde_yaml::from_str(&token).unwrap();
map.get(&t)
}
Value::Sequence(seq) => {
let t: usize = token.parse().unwrap();
seq.get(t)
}
_ => {
None
}
}
})
}
pub fn get_mut<'a>(doc: &'a mut Value, ptr: &'a str) -> Option<&'a mut Value> {
pub fn get<'a>(doc: &'a Value, ptr: &'a str) -> Option<&'a Value> {
if ptr.is_empty() {
panic!("Point is empty");
if !ptr.starts_with('/') {
panic!("{} is not a valid pointer", &ptr);
}
use serde::Deserialize;
use serde_yaml::{self, Value, Mapping, Sequence};
use std::collections::HashMap;
use url::{Url, ParseError};
use serde_yaml::Value;
mod karbonfile;
use karbonfile::*;
mod json_pointer;
use thiserror::Error;
if p.is_file() {
let url = Url::from_file_path(p)
.expect("Unable to convert {p} to a URL");
#[derive(Error, Debug)]
enum Error {
#[error(transparent)]
StdIo(#[from] std::io::Error),
#[error("{0} is not a valid karbonfile path or directory")]
InvalidBuildPath(PathBuf),
#[error(transparent)]
SerdeError(#[from] serde_yaml::Error),
}
return vec!(url);
} else if p.is_dir() {
let entries = fs::read_dir(p.to_owned())
.expect("Unable to list the content of {p}");
let mut result = Vec::new();
#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
enum Document {
Karbonfile {
apiVersion: String,
resources: HashMap<String, Option<ResourceOpts>>,
#[serde(default)]
transformations: Vec<Transformation>,
},
Other(Value),
}
serde_yaml::from_reader(file)
.expect("Failed to parse the file.")
}
_ => {
panic!("Failed to par {url}, remote files are not implemented yet.");
}
#[derive(Clone, Debug, Deserialize)]
#[serde(untagged, rename_all = "lowercase")]
enum Transformation {
PatchSet {
#[serde(default)]
filters: Vec<Filter>,
patches: HashMap<JsonPointer, JsonPatch>,
path.set_extension("yaml");
if path.is_file() {
return to_url(&path);
}
panic!("Failed to find any karbonfile.[yml|yaml] files in {url}");
}
if path.is_file() {
return url.to_owned();
for (ptr, val) in map.into_iter() {
if *ptr.get_mut(doc) != val.clone() {
return false;
fn merge_url(base: &Url, s: &str) -> Url {
base.join(s)
.expect("Failed to merge {url} and {s}")
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
#[serde(try_from = "String")]
struct JsonPointer(Vec<String>);
fn build_variants(
original_url: Url,
mut variant: Variant,
result: &mut VariantVec) {
for field in self.fields() {
result = result
.get_mut(field)
.expect("Field {field} wasn't found");
}
if resources.is_empty() {
leaf_is_karbonfile_without_resources(result, &variant, &url);
} else {
// not a leaf here since it's a karbonfile
// and there is resources to go through.
// So start a new branch an keep walking.
for r in resources {
let next = merge_url(&url, &r);
build_variants(next, variant.clone(), result);
}
}
}
Err(e) => {
leaf_is_not_karbonfile(result, &variant, &url);
impl From<String> for JsonPointer {
fn from(item: String) -> Self {
if !item.starts_with("/") {
panic!("{item} is not a valid JSON pointer. It should start with a '/'");
fn get_resources(karbonfile: &Karbonfile) -> Vec<Resource> {
karbonfile
.resources
.iter()
.map(|k| k.clone())
.collect()
fn read_file(path: &PathBuf) -> String {
return fs::read_to_string(path)
.expect(&format!("Failed to open file {}", path.display()));
fn push_transformations(variant: &mut Variant, url: &Url, karbonfile: &Karbonfile) {
let new = (
url.to_owned(),
karbonfile.transformations.to_owned()
);
match &variant.transformations {
None => {
variant.transformations = Some(
vec!(new)
);
}
Some(t) => {
let mut new_t = t.to_owned();
new_t.push(new);
variant.transformations = Some(new_t);
}
}
return result;
v.original = Some(parse(&url));
v.url = Some(url.to_owned());
v.apply_transformations();
result.push(v);
fn normalize(root: &PathBuf, input: &str) -> PathBuf {
let mut result = root.clone();
if result.is_file() {
result.pop();
};
result.push(input);
return result
.canonicalize()
.expect(&format!("Failed to normalize {}", result.display()));
fn leaf_is_karbonfile_without_resources(result: &mut VariantVec, variant: &Variant, url: &Url) {
// leaf since it's a karbonfile but there
// is no resources in it.
// Set the path and push the branch to result
let mut v = variant.to_owned();
match document {
Document::Karbonfile { resources: resources, .. } => {
let mut branch = variant.branch;
branch.push(file);
v.url = Some(url.to_owned());
result.push(v);
let mut new_variant = Variant {
branch: branch,
..variant
};
for r in resources.keys() {
run(normalize(&path, r), result, new_variant.clone());
};
},
Document::Other(_) => {
let mut new_variant = Variant {
original: Some(file),
..variant
};
result.push(new_variant);
},
};
clap = { version = "3.0", features = ["derive"] }
url = { version = "2", features = ["serde"] }
minreq = { version = "2.5.1", features = ["https", "punycode"] }
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8"
clap = { version = "4.0.18", features = ["derive"] }
serde = { version = "1.0.147", features = ["derive"] }
serde_with = "2.0.1"
serde_yaml = "0.9.14"
thiserror = "1.0.37"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
"matches",
"percent-encoding",
"os_str_bytes",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "cxx"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a"
dependencies = [
"cc",
"cxxbridge-flags",
"cxxbridge-macro",
"link-cplusplus",
]
[[package]]
name = "cxx-build"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827"
dependencies = [
"cc",
"codespan-reporting",
"once_cell",
"proc-macro2",
"quote",
"scratch",
"syn",
]
[[package]]
name = "cxxbridge-flags"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a"
[[package]]
name = "cxxbridge-macro"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "darling"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e"
dependencies = [
"darling_core",
"quote",
"syn",
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "iana-time-zone"
version = "0.1.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
dependencies = [
"cxx",
"cxx-build",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"libc",
"once_cell",
"spin",
"untrusted",
"web-sys",
"winapi",
]
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "scratch"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
name = "web-sys"
version = "0.3.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webpki"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "webpki-roots"
version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449"
dependencies = [
"webpki",
]
[[package]]