This commit is contained in:
JMARyA 2023-11-04 08:22:18 +01:00
commit c4f493966a
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
5 changed files with 893 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

482
Cargo.lock generated Normal file
View file

@ -0,0 +1,482 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "anstream"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "anstyle-parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
dependencies = [
"anstyle",
"windows-sys 0.48.0",
]
[[package]]
name = "clap"
version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_lex"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "console"
version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"unicode-width",
"windows-sys 0.45.0",
]
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "hashbrown"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
[[package]]
name = "indexmap"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "json-patch"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55ff1e1486799e3f64129f8ccad108b38290df9cd7015cd31bed17239f0789d6"
dependencies = [
"serde",
"serde_json",
"thiserror",
"treediff",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "mdpatch"
version = "0.1.0"
dependencies = [
"clap",
"console",
"json-patch",
"regex",
"serde",
"serde_json",
"serde_yaml",
]
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "proc-macro2"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "serde"
version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.9.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "2.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "treediff"
version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303"
dependencies = [
"serde_json",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "unsafe-libyaml"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"

19
Cargo.toml Normal file
View file

@ -0,0 +1,19 @@
[package]
name = "mdpatch"
version = "0.1.0"
edition = "2021"
[profile.release]
strip = true
lto = true
codegen-units = 1
panic = "abort"
[dependencies]
clap = { version = "4.4.7", features = ["cargo"] }
console = "0.15.7"
json-patch = "1.2.0"
regex = "1.10.2"
serde = "1.0.190"
serde_json = "1.0.108"
serde_yaml = "0.9.27"

27
src/args.rs Normal file
View file

@ -0,0 +1,27 @@
use clap::{arg, command, ArgMatches};
pub fn get_args() -> ArgMatches {
command!()
.about("Patch the frontmatter of markdown files")
.arg(arg!([markdown] "Markdown File").required(true))
.arg(arg!(-p --patch <PATCH> "Apply JSON Patch file. If set to '-' read from stdin").required(false).conflicts_with("merge"))
.arg(arg!(--merge <FILE> "Merge Markdown frontmatter. If set to '-' read from stdin").required(false).conflicts_with("patch"))
.arg(arg!(-a --add "Only patch add operations").required(false).conflicts_with("merge"))
.arg(arg!(-m --modify "Only patch modify operations").required(false).conflicts_with("merge"))
.arg(arg!(-d --delete "Only patch delete operations").required(false).conflicts_with("merge"))
.arg(arg!(-v --verbose "Print out what changes will be made to the document").required(false))
.arg(arg!(-n --dryrun "Dont modify the input file. Only print what would be done").required(false))
.arg(arg!(-o --output <OUTPUT> "Write patched file to output path. Dont modify the input file directly. If set to '-' output to stdout"))
.arg(arg!(--notest "Ignore tests in JSON Patch files").conflicts_with("merge"))
.arg(
arg!(-x --exclude <JSONPATH>... "Exclude json path from patch")
.required(false)
.conflicts_with("keys"),
)
.arg(
arg!(-k --keys <JSONPATH>... "Only include the specified json paths in patch")
.required(false)
.conflicts_with("exclude"),
)
.get_matches()
}

364
src/main.rs Normal file
View file

@ -0,0 +1,364 @@
use std::io::Read;
use json_patch::PatchOperation;
use serde_json::Value;
mod args;
trait ErrorExit {
type T;
fn quit_on_error(self, msg: &str) -> Self::T;
}
impl<T, E> ErrorExit for Result<T, E> {
type T = T;
fn quit_on_error(self, msg: &str) -> T {
self.map_or_else(
|_| {
eprintln!("{} {msg}", console::Style::new().red().apply_to("Error:"));
std::process::exit(1);
},
|res| res,
)
}
}
/// seperate markdown string into frontmatter and markdown content
#[must_use]
pub fn markdown_with_frontmatter(markdown: &str) -> (Option<String>, String) {
let frontmatter_regex = regex::Regex::new(r"(?s)^---\s*\n(.*?)\n---\s*\n(.*)$").unwrap();
frontmatter_regex.captures(markdown).map_or_else(
|| (None, markdown.to_string()),
|captures| {
let frontmatter = captures.get(1).map(|m| m.as_str().to_string());
let remaining_markdown = captures
.get(2)
.map(|m| m.as_str().to_string())
.unwrap_or_default();
(frontmatter, remaining_markdown)
},
)
}
fn assemble_markdown_doc(content: &str, frontmatter: serde_json::Value) -> String {
let frontmatter = serde_yaml::to_string(&serde_yaml::to_value(frontmatter).unwrap()).unwrap();
format!("---\n{frontmatter}---\n\n{content}")
}
fn remove_test_operations(patch: Vec<PatchOperation>) -> Vec<PatchOperation> {
patch
.into_iter()
.filter(|x| !matches!(x, json_patch::PatchOperation::Test(_)))
.collect()
}
fn remove_add_operations(patch: Vec<PatchOperation>) -> Vec<PatchOperation> {
patch
.into_iter()
.filter(|x| !matches!(x, json_patch::PatchOperation::Add(_)))
.collect()
}
fn remove_modify_operations(patch: Vec<PatchOperation>) -> Vec<PatchOperation> {
patch
.into_iter()
.filter(|x| {
!matches!(
x,
json_patch::PatchOperation::Replace(_)
| json_patch::PatchOperation::Move(_)
| json_patch::PatchOperation::Copy(_)
)
})
.collect()
}
fn remove_delete_operations(patch: Vec<PatchOperation>) -> Vec<PatchOperation> {
patch
.into_iter()
.filter(|x| !matches!(x, json_patch::PatchOperation::Remove(_)))
.collect()
}
fn exclude_json_paths(patch: &serde_json::Value, paths: &[String]) -> serde_json::Value {
let filtered: Vec<serde_json::Value> = patch
.as_array()
.unwrap()
.iter()
.filter(|x| {
let mut is_excluded = false;
for exclude in paths {
if x.as_object()
.unwrap()
.get("path")
.unwrap()
.as_str()
.unwrap()
.starts_with(exclude)
{
is_excluded = true;
}
}
!is_excluded
})
.cloned()
.collect();
serde_json::to_value(filtered).unwrap()
}
fn include_only_json_paths(patch: &serde_json::Value, paths: &[String]) -> serde_json::Value {
let filtered: Vec<serde_json::Value> = patch
.as_array()
.unwrap()
.iter()
.filter(|x| {
let mut is_included = false;
for include in paths {
if x.as_object()
.unwrap()
.get("path")
.unwrap()
.as_str()
.unwrap()
.starts_with(include)
{
is_included = true;
}
}
is_included
})
.cloned()
.collect();
serde_json::to_value(filtered).unwrap()
}
fn only_include_json_pointers(paths: &[String], json_value: &Value) -> Value {
match json_value {
Value::Object(obj) => {
let mut filtered_obj = serde_json::Map::new();
for path in paths {
if let Some(key) = path.strip_prefix('/') {
if let Some(value) = obj.get(key) {
filtered_obj
.insert(key.to_string(), only_include_json_pointers(paths, value));
}
}
}
Value::Object(filtered_obj)
}
Value::Array(arr) => {
let filtered_arr: Vec<Value> = arr
.iter()
.map(|item| only_include_json_pointers(paths, item))
.collect();
Value::Array(filtered_arr)
}
_ => json_value.clone(),
}
}
fn exclude_json_by_paths(paths: &[String], json_value: &Value) -> Value {
match json_value {
Value::Object(obj) => {
let mut filtered_obj = serde_json::Map::new();
for (key, value) in obj {
if !paths.iter().any(|path| path == &("/".to_string() + key)) {
filtered_obj.insert(key.clone(), exclude_json_by_paths(paths, value));
}
}
Value::Object(filtered_obj)
}
Value::Array(arr) => {
let filtered_arr: Vec<Value> = arr
.iter()
.map(|item| exclude_json_by_paths(paths, item))
.collect();
Value::Array(filtered_arr)
}
_ => json_value.clone(),
}
}
fn main() {
let args = args::get_args();
let markdown_file = args.get_one::<String>("markdown").unwrap();
let (frontmatter, markdown_content) = markdown_with_frontmatter(
&std::fs::read_to_string(markdown_file).quit_on_error("Could not read markdown file"),
);
let frontmatter = frontmatter
.ok_or(0)
.quit_on_error("Could not parse frontmatter");
let mut frontmatter = serde_json::to_value(
&serde_yaml::from_str::<serde_yaml::Value>(&frontmatter)
.quit_on_error("Frontmatter is no valid yaml"),
)
.unwrap();
if let Some(patch_file) = args.get_one::<String>("patch") {
let mut patch: Vec<json_patch::PatchOperation> = serde_json::from_str(
&(if patch_file == "-" {
let mut str = String::new();
std::io::stdin()
.read_to_string(&mut str)
.quit_on_error("Coult not read stdin");
str
} else {
std::fs::read_to_string(patch_file).quit_on_error("Could not read patch file")
}),
)
.quit_on_error("Could not parse patch file");
if args.get_flag("add") | args.get_flag("modify") | args.get_flag("delete") {
if !args.get_flag("add") {
patch = remove_add_operations(patch);
}
if !args.get_flag("modify") {
patch = remove_modify_operations(patch);
}
if !args.get_flag("delete") {
patch = remove_delete_operations(patch);
}
}
if args.get_flag("notest") {
patch = remove_test_operations(patch);
}
let mut patch = serde_json::to_value(patch).unwrap();
if let Some(excludes) = args.get_many::<String>("exclude") {
let excludes: Vec<String> = excludes.cloned().collect();
patch = exclude_json_paths(&patch, &excludes);
}
if let Some(includes) = args.get_many::<String>("keys") {
let includes: Vec<String> = includes.cloned().collect();
patch = include_only_json_paths(&patch, &includes);
}
let patch: Vec<json_patch::PatchOperation> = serde_json::from_value(patch).unwrap();
if args.get_flag("verbose") | args.get_flag("dryrun") {
for op in patch.clone() {
match op {
PatchOperation::Add(op) => {
eprintln!(
"Add {} at {}",
console::Style::new().green().apply_to(op.value),
console::Style::new().bright().red().apply_to(op.path)
);
}
PatchOperation::Remove(op) => {
eprintln!(
"Remove value at {}",
console::Style::new().bright().red().apply_to(op.path)
);
}
PatchOperation::Replace(op) => {
eprintln!(
"Replace value at {} with {}",
console::Style::new().bright().red().apply_to(op.path),
console::Style::new().green().apply_to(op.value)
);
}
PatchOperation::Move(op) => {
eprintln!(
"Move value from {} to {}",
console::Style::new().bright().red().apply_to(op.from),
console::Style::new().green().apply_to(op.path)
);
}
PatchOperation::Copy(op) => {
eprintln!(
"Copy value from {} to {}",
console::Style::new().bright().red().apply_to(op.from),
console::Style::new().green().apply_to(op.path)
);
}
PatchOperation::Test(op) => {
eprintln!(
"Test value at {} for {}",
console::Style::new().bright().red().apply_to(op.path),
console::Style::new().green().apply_to(op.value)
);
}
}
}
}
if !args.get_flag("dryrun") {
if let Err(e) = json_patch::patch(&mut frontmatter, &patch) {
eprintln!(
"{} Patch failed",
console::Style::new().red().apply_to("Error:")
);
eprintln!("{e}");
std::process::exit(1);
}
let markdown_doc = assemble_markdown_doc(&markdown_content, frontmatter);
if let Some(output_path) = args.get_one::<String>("output") {
if output_path == "-" {
print!("{markdown_doc}");
} else {
std::fs::write(output_path, markdown_doc)
.quit_on_error("Could not write output file");
}
} else {
std::fs::write(markdown_file, markdown_doc)
.quit_on_error("Could not write patched file");
}
}
} else if let Some(merge_file) = args.get_one::<String>("merge") {
// TODO : Add support for merging frontmatter from markdown file
let mut merge_doc: serde_json::Value = serde_json::from_str(
&(if merge_file == "-" {
let mut str = String::new();
std::io::stdin()
.read_to_string(&mut str)
.quit_on_error("Coult not read stdin");
str
} else {
std::fs::read_to_string(merge_file).quit_on_error("Could not read merge file")
}),
)
.quit_on_error("Could not parse merge file");
if let Some(excludes) = args.get_many::<String>("exclude") {
let excludes: Vec<String> = excludes.cloned().collect();
merge_doc = exclude_json_by_paths(&excludes, &merge_doc);
}
if let Some(includes) = args.get_many::<String>("keys") {
let includes: Vec<String> = includes.cloned().collect();
merge_doc = only_include_json_pointers(&includes, &merge_doc);
}
if args.get_flag("verbose") | args.get_flag("dryrun") {
eprintln!("Merging {}", serde_json::to_string(&merge_doc).unwrap());
}
if !args.get_flag("dryrun") {
json_patch::merge(&mut frontmatter, &merge_doc);
let markdown_doc = assemble_markdown_doc(&markdown_content, frontmatter);
if let Some(output_path) = args.get_one::<String>("output") {
if output_path == "-" {
print!("{markdown_doc}");
} else {
std::fs::write(output_path, markdown_doc)
.quit_on_error("Could not write output file");
}
} else {
std::fs::write(markdown_file, markdown_doc)
.quit_on_error("Could not write patched file");
}
}
}
}