use alloc::borrow::Cow;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::collections::HashMap;
mod canonical;
mod de;
mod integer;
mod ser;
pub use canonical::KeyOrder;
pub use integer::Integer;
#[derive(Clone, Debug)]
pub enum Error {
Custom(String),
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Error::Custom(msg) => write!(f, "{msg}"),
}
}
}
impl serde::ser::StdError for Error {}
impl serde::de::Error for Error {
#[inline]
fn custom<T: core::fmt::Display>(msg: T) -> Self {
Self::Custom(msg.to_string())
}
}
impl serde::ser::Error for Error {
#[inline]
fn custom<T: core::fmt::Display>(msg: T) -> Self {
Self::Custom(msg.to_string())
}
}
#[non_exhaustive]
#[derive(Clone, PartialEq, PartialOrd)]
pub enum Value {
Integer(Integer),
Bytes(Vec<u8>),
Float(f64),
Text(String),
Bool(bool),
Null,
Tag(u64, Box<Value>),
Array(Vec<Value>),
Map(Vec<(Value, Value)>),
}
impl core::fmt::Debug for Value {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut out = String::new();
crate::diag::write_value_pretty(&mut out, self, 0);
f.write_str(&out)
}
}
macro_rules! accessors {
($(#[doc = $doc:literal] $is:ident $as:ident $into:ident($variant:ident) -> $t:ty;)+) => {
$(
#[doc = concat!("Returns true if the value is ", $doc, ".")]
#[inline]
pub fn $is(&self) -> bool {
matches!(self, Value::$variant(..))
}
#[doc = concat!("If the value is ", $doc, ", returns a reference to it. Returns `None` otherwise.")]
#[inline]
pub fn $as(&self) -> Option<&$t> {
match self {
Value::$variant(x) => Some(x),
_ => None,
}
}
#[doc = concat!("If the value is ", $doc, ", returns it as `Ok`. Returns `Err(self)` otherwise.")]
#[inline]
pub fn $into(self) -> Result<$t, Self> {
match self {
Value::$variant(x) => Ok(x),
other => Err(other),
}
}
)+
};
}
impl Value {
accessors! {
#[doc = "a byte string"]
is_bytes as_bytes into_bytes(Bytes) -> Vec<u8>;
#[doc = "an array"]
is_array as_array into_array(Array) -> Vec<Value>;
#[doc = "a map"]
is_map as_map into_map(Map) -> Vec<(Value, Value)>;
}
#[inline]
pub fn is_integer(&self) -> bool {
matches!(self, Value::Integer(..))
}
#[inline]
pub fn as_integer(&self) -> Option<Integer> {
match self {
Value::Integer(x) => Some(*x),
_ => None,
}
}
#[inline]
pub fn into_integer(self) -> Result<Integer, Self> {
match self {
Value::Integer(x) => Ok(x),
other => Err(other),
}
}
#[inline]
pub fn is_float(&self) -> bool {
matches!(self, Value::Float(..))
}
#[inline]
pub fn as_float(&self) -> Option<f64> {
match self {
Value::Float(x) => Some(*x),
_ => None,
}
}
#[inline]
pub fn into_float(self) -> Result<f64, Self> {
match self {
Value::Float(x) => Ok(x),
other => Err(other),
}
}
#[inline]
pub fn is_text(&self) -> bool {
matches!(self, Value::Text(..))
}
#[inline]
pub fn as_text(&self) -> Option<&str> {
match self {
Value::Text(x) => Some(x),
_ => None,
}
}
#[inline]
pub fn into_text(self) -> Result<String, Self> {
match self {
Value::Text(x) => Ok(x),
other => Err(other),
}
}
#[inline]
pub fn is_bool(&self) -> bool {
matches!(self, Value::Bool(..))
}
#[inline]
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Bool(x) => Some(*x),
_ => None,
}
}
#[inline]
pub fn into_bool(self) -> Result<bool, Self> {
match self {
Value::Bool(x) => Ok(x),
other => Err(other),
}
}
#[inline]
pub fn is_null(&self) -> bool {
matches!(self, Value::Null)
}
#[inline]
pub fn is_tag(&self) -> bool {
matches!(self, Value::Tag(..))
}
#[inline]
pub fn as_tag(&self) -> Option<(u64, &Value)> {
match self {
Value::Tag(tag, data) => Some((*tag, data)),
_ => None,
}
}
#[inline]
pub fn into_tag(self) -> Result<(u64, Box<Value>), Self> {
match self {
Value::Tag(tag, data) => Ok((tag, data)),
other => Err(other),
}
}
#[inline]
pub fn as_bytes_mut(&mut self) -> Option<&mut Vec<u8>> {
match self {
Value::Bytes(x) => Some(x),
_ => None,
}
}
#[inline]
pub fn as_text_mut(&mut self) -> Option<&mut String> {
match self {
Value::Text(x) => Some(x),
_ => None,
}
}
#[inline]
pub fn as_array_mut(&mut self) -> Option<&mut Vec<Value>> {
match self {
Value::Array(x) => Some(x),
_ => None,
}
}
#[inline]
pub fn as_map_mut(&mut self) -> Option<&mut Vec<(Value, Value)>> {
match self {
Value::Map(x) => Some(x),
_ => None,
}
}
#[inline]
pub fn as_tag_mut(&mut self) -> Option<(&mut u64, &mut Value)> {
match self {
Value::Tag(tag, data) => Some((tag, data.as_mut())),
_ => None,
}
}
}
impl core::fmt::Display for Value {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut out = String::new();
crate::diag::write_value(&mut out, self);
f.write_str(&out)
}
}
macro_rules! implfrom {
($($variant:ident($t:ty)),+ $(,)?) => {
$(
impl From<$t> for Value {
#[inline]
fn from(value: $t) -> Self {
Self::$variant(value.into())
}
}
)+
};
}
implfrom! {
Integer(Integer),
Integer(u64),
Integer(i64),
Integer(u32),
Integer(i32),
Integer(u16),
Integer(i16),
Integer(u8),
Integer(i8),
Bytes(Vec<u8>),
Bytes(&[u8]),
Float(f64),
Float(f32),
Text(String),
Text(&str),
Bool(bool),
Array(&[Value]),
Array(Vec<Value>),
Map(&[(Value, Value)]),
Map(Vec<(Value, Value)>),
}
impl From<u128> for Value {
#[inline]
fn from(value: u128) -> Self {
if let Ok(x) = Integer::try_from(value) {
return Value::Integer(x);
}
let mut bytes = &value.to_be_bytes()[..];
while let Some(0) = bytes.first() {
bytes = &bytes[1..];
}
Value::Tag(crate::core::tag::BIGPOS, Value::Bytes(bytes.into()).into())
}
}
impl From<i128> for Value {
#[inline]
fn from(value: i128) -> Self {
if let Ok(x) = Integer::try_from(value) {
return Value::Integer(x);
}
let (tag, raw) = match value.is_negative() {
true => (crate::core::tag::BIGNEG, value as u128 ^ !0),
false => (crate::core::tag::BIGPOS, value as u128),
};
let mut bytes = &raw.to_be_bytes()[..];
while let Some(0) = bytes.first() {
bytes = &bytes[1..];
}
Value::Tag(tag, Value::Bytes(bytes.into()).into())
}
}
impl From<char> for Value {
#[inline]
fn from(value: char) -> Self {
let mut v = String::with_capacity(value.len_utf8());
v.push(value);
Value::Text(v)
}
}
impl From<Cow<'_, str>> for Value {
#[inline]
fn from(value: Cow<'_, str>) -> Self {
Value::Text(value.into_owned())
}
}
impl<const N: usize> From<[u8; N]> for Value {
#[inline]
fn from(value: [u8; N]) -> Self {
Value::Bytes(value.into())
}
}
impl<const N: usize> From<&[u8; N]> for Value {
#[inline]
fn from(value: &[u8; N]) -> Self {
Value::Bytes(value.into())
}
}
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
impl From<usize> for Value {
#[inline]
fn from(value: usize) -> Self {
Value::Integer(value.into())
}
}
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
impl From<isize> for Value {
#[inline]
fn from(value: isize) -> Self {
Value::Integer(value.into())
}
}
impl<T: Into<Value>> From<Option<T>> for Value {
#[inline]
fn from(value: Option<T>) -> Self {
match value {
Some(value) => value.into(),
None => Value::Null,
}
}
}
#[cfg(feature = "std")]
impl<K: Into<Value>, V: Into<Value>> From<HashMap<K, V>> for Value {
fn from(value: HashMap<K, V>) -> Self {
Value::Map(
value
.into_iter()
.map(|(k, v)| (k.into(), v.into()))
.collect(),
)
}
}
impl<K: Into<Value>, V: Into<Value>> From<BTreeMap<K, V>> for Value {
fn from(value: BTreeMap<K, V>) -> Self {
Value::Map(
value
.into_iter()
.map(|(k, v)| (k.into(), v.into()))
.collect(),
)
}
}
impl<T: Into<Value>> FromIterator<T> for Value {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Value::Array(iter.into_iter().map(Into::into).collect())
}
}
fn invalid_type(value: &Value, expected: &'static str) -> Error {
serde::de::Error::invalid_type(value.into(), &expected)
}
macro_rules! tryfrom_value {
($($t:ty: $variant:ident => $expected:literal,)+) => {$(
#[doc = concat!("Converts from [`Value::", stringify!($variant), "`]; any other variant is an `\"invalid type\"` error.")]
impl TryFrom<Value> for $t {
type Error = Error;
#[inline]
fn try_from(value: Value) -> Result<Self, Error> {
match value {
Value::$variant(x) => Ok(x),
other => Err(invalid_type(&other, $expected)),
}
}
}
)+};
}
tryfrom_value! {
Integer: Integer => "integer",
Vec<u8>: Bytes => "bytes",
f64: Float => "float",
String: Text => "text",
bool: Bool => "bool",
Vec<Value>: Array => "array",
Vec<(Value, Value)>: Map => "map",
}
impl TryFrom<Value> for char {
type Error = Error;
fn try_from(value: Value) -> Result<Self, Error> {
if let Value::Text(text) = &value {
let mut chars = text.chars();
if let (Some(c), None) = (chars.next(), chars.next()) {
return Ok(c);
}
}
Err(invalid_type(&value, "a single-character text"))
}
}
macro_rules! tryfrom_int {
($($(#[$($attr:meta)+])? $t:ident)+) => {$(
$(#[$($attr)+])?
#[doc = concat!("Converts from a [`Value::Integer`] in `", stringify!($t), "` range.")]
impl TryFrom<Value> for $t {
type Error = Error;
#[inline]
fn try_from(value: Value) -> Result<Self, Error> {
match value {
Value::Integer(x) => $t::try_from(x).map_err(|_| {
Error::Custom(format!(
concat!(
"invalid value: integer `{}`, expected ",
stringify!($t),
),
i128::from(x),
))
}),
other => Err(invalid_type(&other, "integer")),
}
}
}
)+};
}
tryfrom_int! {
u8 u16 u32 u64
i8 i16 i32 i64
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
usize
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
isize
}
impl TryFrom<Value> for u128 {
type Error = Error;
#[inline]
fn try_from(value: Value) -> Result<Self, Error> {
value.deserialized()
}
}
impl TryFrom<Value> for i128 {
type Error = Error;
#[inline]
fn try_from(value: Value) -> Result<Self, Error> {
value.deserialized()
}
}
fn map_entries<K, V, M>(pairs: Vec<(Value, Value)>) -> Result<M, Error>
where
K: TryFrom<Value>,
K::Error: core::fmt::Display,
V: TryFrom<Value>,
V::Error: core::fmt::Display,
M: FromIterator<(K, V)>,
{
pairs
.into_iter()
.map(|(k, v)| {
Ok((
K::try_from(k).map_err(|err| Error::Custom(format!("invalid map key: {err}")))?,
V::try_from(v).map_err(|err| Error::Custom(format!("invalid map value: {err}")))?,
))
})
.collect()
}
#[cfg(feature = "std")]
impl<K, V> TryFrom<Value> for HashMap<K, V>
where
K: TryFrom<Value> + Eq + core::hash::Hash,
K::Error: core::fmt::Display,
V: TryFrom<Value>,
V::Error: core::fmt::Display,
{
type Error = Error;
fn try_from(value: Value) -> Result<Self, Error> {
match value {
Value::Map(pairs) => map_entries(pairs),
other => Err(invalid_type(&other, "map")),
}
}
}
impl<K, V> TryFrom<Value> for BTreeMap<K, V>
where
K: TryFrom<Value> + Ord,
K::Error: core::fmt::Display,
V: TryFrom<Value>,
V::Error: core::fmt::Display,
{
type Error = Error;
fn try_from(value: Value) -> Result<Self, Error> {
match value {
Value::Map(pairs) => map_entries(pairs),
other => Err(invalid_type(&other, "map")),
}
}
}