1use crate::json::{self, Value};
2
3#[derive(Clone, Debug, PartialEq, Eq)]
4pub struct Error {
5 pub path: String,
6 pub kind: ErrorKind,
7}
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum ErrorKind {
11 Json(json::Error),
12 MissingField,
13 UnknownField,
14 WrongType,
15 InvalidValue,
16 UnsortedSet,
17 DuplicateMember,
18 LimitExceeded,
19 DigestMismatch,
20 Inconsistent,
21}
22
23impl Error {
24 #[must_use]
25 pub fn new(path: &str, kind: ErrorKind) -> Self {
26 Self {
27 path: path.to_owned(),
28 kind,
29 }
30 }
31}
32
33pub fn fail<T>(path: &str, kind: ErrorKind) -> Result<T, Error> {
37 Err(Error::new(path, kind))
38}
39
40pub struct Obj {
41 path: String,
42 members: Vec<(String, Value)>,
43}
44
45impl Obj {
46 pub fn new(path: &str, value: Value) -> Result<Self, Error> {
50 match value {
51 Value::Object(members) => Ok(Self {
52 path: path.to_owned(),
53 members,
54 }),
55 Value::Null
56 | Value::Bool(_)
57 | Value::Integer(_)
58 | Value::String(_)
59 | Value::Array(_) => fail(path, ErrorKind::WrongType),
60 }
61 }
62
63 #[must_use]
64 pub fn field(&self, name: &str) -> String {
65 format!("{}.{name}", self.path)
66 }
67
68 pub fn take(&mut self, name: &str) -> Result<Value, Error> {
72 match self.members.iter().position(|(key, _)| key == name) {
73 Some(index) => Ok(self.members.remove(index).1),
74 None => fail(&self.field(name), ErrorKind::MissingField),
75 }
76 }
77
78 pub fn finish(self) -> Result<(), Error> {
82 match self.members.into_iter().next() {
83 None => Ok(()),
84 Some((name, _)) => Err(Error {
85 kind: ErrorKind::UnknownField,
86 path: format!("{}.{name}", self.path),
87 }),
88 }
89 }
90}
91
92pub fn string(path: &str, value: Value) -> Result<String, Error> {
96 match value {
97 Value::String(s) => Ok(s),
98 Value::Null | Value::Bool(_) | Value::Integer(_) | Value::Array(_) | Value::Object(_) => {
99 fail(path, ErrorKind::WrongType)
100 }
101 }
102}
103
104#[expect(
108 clippy::needless_pass_by_value,
109 reason = "uniform consuming decoder signature"
110)]
111pub fn integer(path: &str, value: Value) -> Result<i64, Error> {
112 match value {
113 Value::Integer(n) => Ok(n),
114 Value::Null | Value::Bool(_) | Value::String(_) | Value::Array(_) | Value::Object(_) => {
115 fail(path, ErrorKind::WrongType)
116 }
117 }
118}
119
120pub fn array(path: &str, value: Value) -> Result<Vec<Value>, Error> {
124 match value {
125 Value::Array(items) => Ok(items),
126 Value::Null | Value::Bool(_) | Value::Integer(_) | Value::String(_) | Value::Object(_) => {
127 fail(path, ErrorKind::WrongType)
128 }
129 }
130}
131
132pub fn const_str(path: &str, value: Value, expected: &str) -> Result<(), Error> {
136 if string(path, value)? == expected {
137 Ok(())
138 } else {
139 fail(path, ErrorKind::InvalidValue)
140 }
141}
142
143#[must_use]
144pub fn nullable(value: Value) -> Option<Value> {
145 match value {
146 Value::Null => None,
147 other @ (Value::Bool(_)
148 | Value::Integer(_)
149 | Value::String(_)
150 | Value::Array(_)
151 | Value::Object(_)) => Some(other),
152 }
153}