use base64::Engine;
use serde_json::{Map, Value};
use crate::{BencodexKey, BencodexValue};
fn format_key(key: &BencodexKey, options: &JsonEncodeOptions) -> String {
match key {
BencodexKey::Binary(data) => match options.binary_encoding {
BinaryEncoding::Base64 => {
format!(
"b64:{}",
base64::engine::general_purpose::STANDARD.encode(data)
)
}
BinaryEncoding::Hex => format!("0x{}", hex::encode(data)),
},
BencodexKey::Text(text) => format!("\u{FEFF}{}", text),
}
}
fn encode_value(value: &BencodexValue, options: &JsonEncodeOptions) -> Value {
match value {
BencodexValue::Null => Value::Null,
BencodexValue::Boolean(b) => Value::Bool(*b),
BencodexValue::Number(n) => Value::String(n.to_string()),
BencodexValue::Binary(data) => {
Value::String(format_key(&BencodexKey::Binary(data.clone()), options))
}
BencodexValue::Text(text) => {
Value::String(format_key(&BencodexKey::Text(text.clone()), options))
}
BencodexValue::List(items) => {
Value::Array(items.iter().map(|v| encode_value(v, options)).collect())
}
BencodexValue::Dictionary(map) => {
let obj: Map<String, Value> = map
.iter()
.map(|(k, v)| (format_key(k, options), encode_value(v, options)))
.collect();
Value::Object(obj)
}
}
}
pub enum BinaryEncoding {
Base64,
Hex,
}
impl Default for BinaryEncoding {
fn default() -> Self {
Self::Base64
}
}
#[derive(Default)]
pub struct JsonEncodeOptions {
pub binary_encoding: BinaryEncoding,
}
pub fn to_json(value: &BencodexValue) -> String {
to_json_with_options(value, JsonEncodeOptions::default())
}
pub fn to_json_with_options(value: &BencodexValue, options: JsonEncodeOptions) -> String {
let json_value = encode_value(value, &options);
serde_json::to_string(&json_value).unwrap()
}