Skip to main content

pipa/value/
format.rs

1// SPDX-FileCopyrightText: Copyright 2026 olav@occy.org
2// SPDX-License-Identifier: MPL-2.0
3
4//! Format values as JSON or YAML.
5
6use crate::serde::byte_keys_as_strings;
7use crate::serde::bytes_as_string;
8use crate::value::Value;
9use crate::value::error::Error;
10use crate::value::list::List;
11use crate::value::map::Map;
12use indexmap::IndexMap;
13use serde::Serialize;
14
15pub fn json(value: Value) -> Result<Vec<u8>, Error> {
16    serde_json::to_vec(&Strategy::new(value)).map_err(|e| Error::Format(e.to_string()))
17}
18
19pub fn json_pretty(value: Value) -> Result<Vec<u8>, Error> {
20    serde_json::to_vec_pretty(&Strategy::new(value)).map_err(|e| Error::Format(e.to_string()))
21}
22
23pub fn json_string(value: Value) -> Result<String, Error> {
24    serde_json::to_string(&Strategy::new(value)).map_err(|e| Error::Format(e.to_string()))
25}
26
27pub fn json_string_pretty(value: Value) -> Result<String, Error> {
28    serde_json::to_string_pretty(&Strategy::new(value)).map_err(|e| Error::Format(e.to_string()))
29}
30
31pub fn yaml(value: Value) -> Result<String, Error> {
32    serde_yaml::to_string(&Strategy::new(value)).map_err(|e| Error::Format(e.to_string()))
33}
34
35#[derive(Serialize)]
36#[serde(untagged)]
37enum Strategy {
38    Default(Box<Value>),
39    Simple(Simple),
40}
41
42impl Strategy {
43    fn new(value: Value) -> Strategy {
44        match value.meta().get(b"internal-serialize") == Some(b"simple") {
45            true => Strategy::Simple(Simple::new(value)),
46            false => Strategy::Default(Box::new(value)),
47        }
48    }
49}
50
51#[derive(Serialize)]
52#[serde(untagged)]
53enum Simple {
54    Bytes(SimpleBytes),
55    List(SimpleList),
56    Map(SimpleMap),
57}
58
59impl Simple {
60    fn new(value: Value) -> Self {
61        match value {
62            Value::List(v) if should_concat(&value) => Simple::Bytes(SimpleBytes(v.join())),
63            Value::Map(v) if should_concat(&value) => Simple::Bytes(SimpleBytes(v.join())),
64            Value::Bytes(v) => Simple::Bytes(SimpleBytes(v.join())),
65            Value::List(v) => Simple::List(SimpleList::new(v)),
66            Value::Map(v) => Simple::Map(SimpleMap::new(v)),
67        }
68    }
69}
70
71#[derive(Serialize)]
72struct SimpleBytes(#[serde(with = "bytes_as_string")] Vec<u8>);
73
74#[derive(Serialize)]
75struct SimpleList(Vec<Simple>);
76
77impl SimpleList {
78    fn new(value: List) -> Self {
79        SimpleList(value.data.into_iter().map(Simple::new).collect())
80    }
81}
82
83#[derive(Serialize)]
84pub(crate) struct SimpleMap(
85    #[serde(with = "byte_keys_as_strings")] IndexMap<Vec<u8>, Simple, ahash::RandomState>,
86);
87
88impl SimpleMap {
89    fn new(value: Map) -> Self {
90        SimpleMap(
91            value
92                .data
93                .into_iter()
94                .map(|(k, v)| (k, Simple::new(v)))
95                .collect(),
96        )
97    }
98}
99
100fn should_concat(value: &Value) -> bool {
101    value.meta().get(b"internal-format") == Some(b"concat")
102}