pub fn format_json_field(k: &str, v: &str, indent: &str) -> String {
let key = k.trim_matches('"');
let val_str = v.trim();
if val_str.is_empty() { return String::new(); }
let is_raw = val_str == "true" || val_str == "false" || val_str == "null" ||
val_str.parse::<f64>().is_ok() || val_str.starts_with('{') || val_str.starts_with('[');
let formatted = if is_raw {
format!("\"{}\": {}", key, val_str)
} else {
format!("\"{}\": \"{}\"", key, val_str)
};
format!("{}{}", indent, formatted)
}
#[macro_export]
macro_rules! json_muncher {
($m:expr, $d:expr, $tag:ident, [$($children:expr),*], ) => {{
let mut fields = Vec::new();
$(
let child_str = format!("{}", $children);
if !child_str.trim().is_empty() { fields.push(child_str); }
)*
let current_indent = match $m { 2 => " ".repeat($d), 4 => " ".repeat($d), _ => String::new() };
let nl = if $m > 0 { "\n" } else { "" };
let tag_name = stringify!($tag);
let (open, close) = if tag_name == "arr" || tag_name == "list" { ("[", "]") } else { ("{", "}") };
if fields.is_empty() {
format!("{}{}", open, close)
} else {
let inner = fields.join(&format!(",{}", nl));
format!("{}{}{}{}{}{}", open, nl, inner, nl, current_indent, close)
}
}};
($m:expr, $d:expr, $tag:ident, [$($children:expr),*], for $var:pat in $collection:expr => { $it:ident { $($ic:tt)* } } $($rest:tt)*) => {{
let mut items = Vec::new();
let child_indent = match $m { 2 => " ".repeat($d + 1), 4 => " ".repeat($d + 1), _ => String::new() };
for $var in $collection {
let inner = $crate::json_muncher!($m, $d + 1, $it, [], $($ic)*).trim().to_string();
items.push(format!("{}{}", child_indent, inner));
}
let joined_items = items.join(&format!(",{}", if $m > 0 { "\n" } else { "" }));
$crate::json_muncher!($m, $d, $tag, [$($children,)* joined_items], $($rest)*)
}};
($m:expr, $d:expr, $tag:ident, [$($children:expr),*], if $cond:expr => { $key:tt : $it:ident { $($ic:tt)* } } else { $e_key:tt : $e_it:ident { $($e_ic:tt)* } } $($rest:tt)*) => {{
let child_indent = match $m { 2 => " ".repeat($d + 1), 4 => " ".repeat($d + 1), _ => String::new() };
let result = if $cond {
let inner = $crate::json_muncher!($m, $d + 1, $it, [], $($ic)*).trim().to_string();
format!("{}\"{}\": {}", child_indent, stringify!($key).trim_matches('"'), inner)
} else {
let inner = $crate::json_muncher!($m, $d + 1, $e_it, [], $($e_ic)*).trim().to_string();
format!("{}\"{}\": {}", child_indent, stringify!($e_key).trim_matches('"'), inner)
};
$crate::json_muncher!($m, $d, $tag, [$($children,)* result], $($rest)*)
}};
($m:expr, $d:expr, $tag:ident, [$($children:expr),*], if $cond:expr => { $key:tt : $it:ident { $($ic:tt)* } } $($rest:tt)*) => {{
let mut result = String::new();
if $cond {
let child_indent = match $m { 2 => " ".repeat($d + 1), 4 => " ".repeat($d + 1), _ => String::new() };
let inner = $crate::json_muncher!($m, $d + 1, $it, [], $($ic)*).trim().to_string();
result = format!("{}\"{}\": {}", child_indent, stringify!($key).trim_matches('"'), inner);
}
$crate::json_muncher!($m, $d, $tag, [$($children,)* result], $($rest)*)
}};
($m:expr, $d:expr, $tag:ident, [$($children:expr),*], $inner_key:tt : $inner_tag:ident { $($inner_content:tt)* } $($rest:tt)*) => {{
let child_indent = match $m { 2 => " ".repeat($d + 1), 4 => " ".repeat($d + 1), _ => String::new() };
let inner = $crate::json_muncher!($m, $d + 1, $inner_tag, [], $($inner_content)*).trim().to_string();
let val = format!("{}\"{}\": {}", child_indent, stringify!($inner_key).trim_matches('"'), inner);
$crate::json_muncher!($m, $d, $tag, [$($children,)* val], $($rest)*)
}};
($m:expr, $d:expr, $tag:ident, [$($children:expr),*], $key:tt : $val:expr, $($rest:tt)+) => {{
let child_indent = match $m { 2 => " ".repeat($d + 1), 4 => " ".repeat($d + 1), _ => String::new() };
let f = $crate::rules::format_json_field(stringify!($key), &format!("{}", $val), &child_indent);
$crate::json_muncher!($m, $d, $tag, [$($children,)* f], $($rest)*)
}};
($m:expr, $d:expr, $tag:ident, [$($children:expr),*], $key:tt : $val:expr) => {{
let child_indent = match $m { 2 => " ".repeat($d + 1), 4 => " ".repeat($d + 1), _ => String::new() };
let f = $crate::rules::format_json_field(stringify!($key), &format!("{}", $val), &child_indent);
$crate::json_muncher!($m, $d, $tag, [$($children,)* f], )
}};
($m:expr, $d:expr, $tag:ident, [$($children:expr),*], $text:literal $($rest:tt)*) => {{
let child_indent = match $m { 2 => " ".repeat($d + 1), 4 => " ".repeat($d + 1), _ => String::new() };
$crate::json_muncher!($m, $d, $tag, [$($children,)* format!("{}\"{}\"", child_indent, $text)], $($rest)*)
}};
($m:expr, $d:expr, $tag:ident, [$($children:expr),*], { $text:expr } $($rest:tt)*) => {
$crate::json_muncher!($m, $d, $tag, [$($children,)* $text], $($rest)*)
};
($m:expr, $d:expr, $tag:ident, [$($children:expr),*], , $($rest:tt)*) => {
$crate::json_muncher!($m, $d, $tag, [$($children),*], $($rest)*)
};
}
#[macro_export]
macro_rules! rsj {
($m_name:ident, $tag:ident { $($content:tt)* }) => {{
let m = match stringify!($m_name) {
"lined" => 0,
"tabed" | "btfy2" => 2,
"btfy4" => 4,
_ => 0,
};
$crate::json_muncher!(m, 0, $tag, [], $($content)*)
}};
}