taplo_cli/commands/
toml_test.rs1use crate::Taplo;
2use anyhow::anyhow;
3use serde::{
4 ser::{SerializeMap, SerializeSeq},
5 Serialize,
6};
7use taplo::dom::{
8 node::{DateTimeValue, DomNode},
9 Node,
10};
11use taplo_common::environment::Environment;
12use tokio::io::AsyncReadExt;
13
14impl<E: Environment> Taplo<E> {
15 pub async fn execute_toml_test(&self) -> Result<(), anyhow::Error> {
16 let mut buf = String::new();
17 self.env.stdin().read_to_string(&mut buf).await?;
18
19 let parse = taplo::parser::parse(&buf);
20
21 if !parse.errors.is_empty() {
22 for err in parse.errors {
23 eprintln!("{err}");
24 }
25
26 return Err(anyhow!("invalid toml"));
27 }
28 let dom = parse.into_dom();
29
30 if let Err(err) = dom.validate() {
31 for err in err {
32 eprintln!("{err}");
33 }
34 return Err(anyhow!("invalid toml"));
35 }
36
37 serde_json::to_writer(std::io::stdout(), &TomlTestValue::new(&dom))?;
38
39 Ok(())
40 }
41}
42
43#[derive(Clone, Copy, Serialize)]
44#[serde(rename_all = "lowercase")]
45pub enum TomlTestType {
46 String,
47 Integer,
48 Float,
49 Bool,
50 DateTime,
51 #[serde(rename = "datetime-local")]
52 DateTimeLocal,
53 #[serde(rename = "date-local")]
54 DateLocal,
55 #[serde(rename = "time-local")]
56 TimeLocal,
57}
58
59impl TomlTestType {
60 fn of(node: &Node) -> Option<Self> {
61 match node {
62 Node::Bool(_) => Some(TomlTestType::Bool),
63 Node::Integer(_) => Some(TomlTestType::Integer),
64 Node::Float(_) => Some(TomlTestType::Float),
65 Node::Str(_) => Some(TomlTestType::String),
66 Node::Date(d) => match d.value() {
67 DateTimeValue::OffsetDateTime(_) => Some(TomlTestType::DateTime),
68 DateTimeValue::LocalDateTime(_) => Some(TomlTestType::DateTimeLocal),
69 DateTimeValue::Date(_) => Some(TomlTestType::DateLocal),
70 DateTimeValue::Time(_) => Some(TomlTestType::TimeLocal),
71 },
72 Node::Array(_) => None,
73 Node::Table(_) => None,
74 Node::Invalid(_) => unreachable!(),
75 }
76 }
77}
78
79pub struct TomlTestValue<'a> {
80 r#type: Option<TomlTestType>,
81 node: &'a Node,
82}
83
84impl<'a> TomlTestValue<'a> {
85 pub fn new(node: &'a Node) -> Self {
86 Self {
87 r#type: TomlTestType::of(node),
88 node,
89 }
90 }
91}
92
93impl Serialize for TomlTestValue<'_> {
94 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
95 where
96 S: serde::Serializer,
97 {
98 if let Some(ty) = self.r#type {
99 let mut map = serializer.serialize_map(Some(2))?;
100 map.serialize_entry("type", &ty)?;
101 map.serialize_entry(
102 "value",
103 &match self.node {
104 Node::Str(d) => d.value().to_string(),
105 Node::Float(f) if f.value().is_nan() => String::from("nan"),
106 Node::Float(f) if f.value().is_infinite() => f.syntax().unwrap().to_string(),
107 _ => serde_json::to_string(&self.node).map_err(serde::ser::Error::custom)?,
108 },
109 )?;
110 map.end()
111 } else {
112 match &self.node {
113 Node::Array(array) => {
114 let items = array.items().read();
115
116 let mut seq = serializer.serialize_seq(Some(items.len()))?;
117 for value in &**items {
118 seq.serialize_element(&TomlTestValue::new(value))?;
119 }
120 seq.end()
121 }
122 Node::Table(table) => {
123 let entries = table.entries().read();
124
125 let mut map = serializer.serialize_map(Some(entries.len()))?;
126 for (key, value) in entries.iter() {
127 map.serialize_entry(key.value(), &TomlTestValue::new(value))?;
128 }
129 map.end()
130 }
131 _ => unreachable!(),
132 }
133 }
134 }
135}