blob: 2cde1e3b1c68e6496b577646f73eabb6f3588261 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::error::{Error, Result};
/// An empty type.
#[derive(Debug)]
pub enum Forbid {}
impl std::fmt::Display for Forbid {
fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
unreachable!()
}
}
/// Our internal neutral form for FIDL values.
#[derive(Debug)]
pub enum Value<OutOfLine = Forbid> {
Null,
Bool(bool),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
F32(f32),
F64(f64),
String(String),
Object(Vec<(String, Self)>),
Bits(String, Box<Self>),
Enum(String, Box<Self>),
Union(String, String, Box<Self>),
List(Vec<Self>),
ServerEnd(fidl::Channel, String),
ClientEnd(fidl::Channel, String),
Handle(fidl::Handle, fidl::ObjectType),
OutOfLine(OutOfLine),
}
impl Value {
/// Get this value as a u64 if it is any integer type. Useful for treating values as bit masks,
/// as we'd like to for the Bits type.
pub(crate) fn bits(&self) -> Option<u64> {
match self {
Value::U8(x) => Some(*x as u64),
Value::U16(x) => Some(*x as u64),
Value::U32(x) => Some(*x as u64),
Value::U64(x) => Some(*x),
Value::I8(x) => Some(u8::from_le_bytes(x.to_le_bytes()) as u64),
Value::I16(x) => Some(u16::from_le_bytes(x.to_le_bytes()) as u64),
Value::I32(x) => Some(u32::from_le_bytes(x.to_le_bytes()) as u64),
Value::I64(x) => Some(u64::from_le_bytes(x.to_le_bytes())),
_ => None,
}
}
/// See if this value is equal to another. Unlike our PartialEq implementation, this will
/// upcast numeric types and compare them.
pub(crate) fn cast_equals(&self, other: &Value) -> bool {
match self {
Value::U8(x) => u8::try_from(other).map(|y| *x == y).unwrap_or(false),
Value::U16(x) => u16::try_from(other).map(|y| *x == y).unwrap_or(false),
Value::U32(x) => u32::try_from(other).map(|y| *x == y).unwrap_or(false),
Value::U64(x) => u64::try_from(other).map(|y| *x == y).unwrap_or(false),
Value::I8(x) => i8::try_from(other).map(|y| *x == y).unwrap_or(false),
Value::I16(x) => i16::try_from(other).map(|y| *x == y).unwrap_or(false),
Value::I32(x) => i32::try_from(other).map(|y| *x == y).unwrap_or(false),
Value::I64(x) => i64::try_from(other).map(|y| *x == y).unwrap_or(false),
Value::F32(x) => f32::try_from(other).map(|y| *x == y).unwrap_or(false),
Value::F64(x) => f64::try_from(other).map(|y| *x == y).unwrap_or(false),
_ => self == other,
}
}
/// Cast a `Value<Forbid>` to a `Value<T>` for any `T`
pub fn upcast<T>(self) -> Value<T> {
match self {
Value::Null => Value::Null,
Value::Bool(a) => Value::Bool(a),
Value::U8(a) => Value::U8(a),
Value::U16(a) => Value::U16(a),
Value::U32(a) => Value::U32(a),
Value::U64(a) => Value::U64(a),
Value::I8(a) => Value::I8(a),
Value::I16(a) => Value::I16(a),
Value::I32(a) => Value::I32(a),
Value::I64(a) => Value::I64(a),
Value::F32(a) => Value::F32(a),
Value::F64(a) => Value::F64(a),
Value::String(a) => Value::String(a),
Value::Object(a) => {
Value::Object(a.into_iter().map(|(a, b)| (a, b.upcast())).collect())
}
Value::Bits(a, b) => Value::Bits(a, Box::new(b.upcast())),
Value::Enum(a, b) => Value::Enum(a, Box::new(b.upcast())),
Value::Union(a, b, c) => Value::Union(a, b, Box::new(c.upcast())),
Value::List(a) => Value::List(a.into_iter().map(Value::upcast).collect()),
Value::ServerEnd(a, b) => Value::ServerEnd(a, b),
Value::ClientEnd(a, b) => Value::ClientEnd(a, b),
Value::Handle(a, b) => Value::Handle(a, b),
Value::OutOfLine(_) => unreachable!(),
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
(Self::U8(l0), Self::U8(r0)) => l0 == r0,
(Self::U16(l0), Self::U16(r0)) => l0 == r0,
(Self::U32(l0), Self::U32(r0)) => l0 == r0,
(Self::U64(l0), Self::U64(r0)) => l0 == r0,
(Self::I8(l0), Self::I8(r0)) => l0 == r0,
(Self::I16(l0), Self::I16(r0)) => l0 == r0,
(Self::I32(l0), Self::I32(r0)) => l0 == r0,
(Self::I64(l0), Self::I64(r0)) => l0 == r0,
(Self::F32(l0), Self::F32(r0)) => l0 == r0,
(Self::F64(l0), Self::F64(r0)) => l0 == r0,
(Self::String(l0), Self::String(r0)) => l0 == r0,
(Self::Object(l0), Self::Object(r0)) => {
let mut values = std::collections::HashMap::new();
for (k, v) in l0 {
if values.insert(k, v).is_some() {
return false;
}
}
for (k, v) in r0 {
let Some(other) = values.remove(&k) else {
return false;
};
if v != other {
return false;
}
}
true
}
(Self::Bits(l0, l1), Self::Bits(r0, r1)) => l0 == r0 && l1 == r1,
(Self::Enum(l0, l1), Self::Enum(r0, r1)) => l0 == r0 && l1 == r1,
(Self::Union(l0, l1, l2), Self::Union(r0, r1, r2)) => l0 == r0 && l1 == r1 && l2 == r2,
(Self::List(l0), Self::List(r0)) => l0 == r0,
(Self::ServerEnd(l0, l1), Self::ServerEnd(r0, r1)) => l0 == r0 && l1 == r1,
(Self::ClientEnd(l0, l1), Self::ClientEnd(r0, r1)) => l0 == r0 && l1 == r1,
(Self::Handle(l0, l1), Self::Handle(r0, r1)) => l0 == r0 && l1 == r1,
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
impl From<&str> for Value {
fn from(v: &str) -> Value {
Value::from(v.to_owned())
}
}
impl From<String> for Value {
fn from(v: String) -> Value {
Value::String(v)
}
}
impl From<bool> for Value {
fn from(v: bool) -> Value {
Value::Bool(v)
}
}
impl From<u8> for Value {
fn from(v: u8) -> Value {
Value::U8(v)
}
}
impl From<u16> for Value {
fn from(v: u16) -> Value {
Value::U16(v)
}
}
impl From<u32> for Value {
fn from(v: u32) -> Value {
Value::U32(v)
}
}
impl From<u64> for Value {
fn from(v: u64) -> Value {
Value::U64(v)
}
}
impl From<i8> for Value {
fn from(v: i8) -> Value {
Value::I8(v)
}
}
impl From<i16> for Value {
fn from(v: i16) -> Value {
Value::I16(v)
}
}
impl From<i32> for Value {
fn from(v: i32) -> Value {
Value::I32(v)
}
}
impl From<i64> for Value {
fn from(v: i64) -> Value {
Value::I64(v)
}
}
impl From<f32> for Value {
fn from(v: f32) -> Value {
Value::F32(v)
}
}
impl From<f64> for Value {
fn from(v: f64) -> Value {
Value::F64(v)
}
}
impl<T> From<Vec<T>> for Value
where
Value: From<T>,
{
fn from(v: Vec<T>) -> Value {
Value::List(v.into_iter().map(Value::from).collect())
}
}
impl<T> From<Option<T>> for Value
where
Value: From<T>,
{
fn from(v: Option<T>) -> Value {
v.map(Value::from).unwrap_or(Value::Null)
}
}
impl<A> FromIterator<A> for Value
where
Value: From<A>,
{
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Value {
iter.into_iter().collect::<Vec<_>>().into()
}
}
macro_rules! try_from_int {
($int:ident) => {
impl TryFrom<&mut Value> for $int {
type Error = $crate::error::Error;
fn try_from(value: &mut Value) -> Result<$int> {
Self::try_from(&*value)
}
}
impl TryFrom<&Value> for $int {
type Error = $crate::error::Error;
fn try_from(value: &Value) -> Result<$int> {
match value {
Value::U8(x) => $int::try_from(*x).ok(),
Value::U16(x) => $int::try_from(*x).ok(),
Value::U32(x) => $int::try_from(*x).ok(),
Value::U64(x) => $int::try_from(*x).ok(),
Value::I8(x) => $int::try_from(*x).ok(),
Value::I16(x) => $int::try_from(*x).ok(),
Value::I32(x) => $int::try_from(*x).ok(),
Value::I64(x) => $int::try_from(*x).ok(),
_ => None,
}
.ok_or(Error::ValueError(format!("Cannot convert to {}", stringify!($int))))
}
}
impl TryFrom<Value> for $int {
type Error = $crate::error::Error;
fn try_from(value: Value) -> Result<$int> {
$int::try_from(&value)
}
}
};
}
try_from_int!(u8);
try_from_int!(u16);
try_from_int!(u32);
try_from_int!(u64);
try_from_int!(i8);
try_from_int!(i16);
try_from_int!(i32);
try_from_int!(i64);
impl TryFrom<&mut Value> for bool {
type Error = crate::error::Error;
fn try_from(value: &mut Value) -> Result<bool> {
bool::try_from(&*value)
}
}
impl TryFrom<&Value> for bool {
type Error = crate::error::Error;
fn try_from(value: &Value) -> Result<bool> {
match value {
Value::Bool(x) => Ok(*x),
Value::U8(x) => Ok(*x != 0),
Value::U16(x) => Ok(*x != 0),
Value::U32(x) => Ok(*x != 0),
Value::U64(x) => Ok(*x != 0),
Value::I8(x) => Ok(*x != 0),
Value::I16(x) => Ok(*x != 0),
Value::I32(x) => Ok(*x != 0),
Value::I64(x) => Ok(*x != 0),
_ => Err(Error::ValueError(format!("Cannot convert {} to bool", value))),
}
}
}
impl TryFrom<Value> for bool {
type Error = Error;
fn try_from(value: Value) -> Result<bool> {
bool::try_from(&value)
}
}
impl TryFrom<&mut Value> for f32 {
type Error = crate::error::Error;
fn try_from(value: &mut Value) -> Result<f32> {
f32::try_from(&*value)
}
}
impl TryFrom<&Value> for f32 {
type Error = Error;
fn try_from(value: &Value) -> Result<f32> {
match value {
Value::U8(x) => Ok(*x as f32),
Value::U16(x) => Ok(*x as f32),
Value::U32(x) => Ok(*x as f32),
Value::U64(x) => Ok(*x as f32),
Value::I8(x) => Ok(*x as f32),
Value::I16(x) => Ok(*x as f32),
Value::I32(x) => Ok(*x as f32),
Value::I64(x) => Ok(*x as f32),
Value::F32(x) => Ok(*x),
Value::F64(x) => Ok(*x as f32),
_ => Err(Error::ValueError(format!("Cannot convert {} to f32", value))),
}
}
}
impl TryFrom<Value> for f32 {
type Error = Error;
fn try_from(value: Value) -> Result<f32> {
f32::try_from(&value)
}
}
impl TryFrom<&mut Value> for f64 {
type Error = crate::error::Error;
fn try_from(value: &mut Value) -> Result<f64> {
f64::try_from(&*value)
}
}
impl TryFrom<&Value> for f64 {
type Error = Error;
fn try_from(value: &Value) -> Result<f64> {
match value {
Value::U8(x) => Ok(*x as f64),
Value::U16(x) => Ok(*x as f64),
Value::U32(x) => Ok(*x as f64),
Value::U64(x) => Ok(*x as f64),
Value::I8(x) => Ok(*x as f64),
Value::I16(x) => Ok(*x as f64),
Value::I32(x) => Ok(*x as f64),
Value::I64(x) => Ok(*x as f64),
Value::F32(x) => Ok(*x as f64),
Value::F64(x) => Ok(*x),
_ => Err(Error::ValueError(format!("Cannot convert {} to f32", value))),
}
}
}
impl TryFrom<Value> for f64 {
type Error = Error;
fn try_from(value: Value) -> Result<f64> {
f64::try_from(&value)
}
}
macro_rules! write_list {
($f:ident, $pattern:pat, $members:expr, $element:expr, $open:expr, $close:expr) => {{
$open;
let mut first = true;
for $pattern in $members {
if !first {
write!($f, ", ")?
} else {
first = false;
write!($f, " ")?;
}
$element;
}
if !first {
write!($f, "{}", " ")?;
}
write!($f, "{}", $close)
}};
}
impl<T: std::fmt::Display> std::fmt::Display for Value<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Value::*;
match self {
Null => write!(f, "null"),
Bool(true) => write!(f, "true"),
Bool(false) => write!(f, "false"),
U8(x) => write!(f, "{}u8", x),
U16(x) => write!(f, "{}u16", x),
U32(x) => write!(f, "{}u32", x),
U64(x) => write!(f, "{}u64", x),
I8(x) => write!(f, "{}i8", x),
I16(x) => write!(f, "{}i16", x),
I32(x) => write!(f, "{}i32", x),
I64(x) => write!(f, "{}i64", x),
F32(x) => write!(f, "{}f32", x),
F64(x) => write!(f, "{}f64", x),
String(x) => write!(f, "\"{}\"", x),
Object(members) => write_list!(
f,
(name, value),
members,
write!(f, "\"{}\": {}", name, value)?,
write!(f, "{}", "{")?,
"}"
),
Bits(ty, value) => write!(f, "{}({})", ty, value),
Enum(ty, value) => write!(f, "{}({})", ty, value),
Union(name, field, value) => write!(f, "{}::{}({})", name, field, value),
List(values) => {
write_list!(f, value, values, write!(f, "{}", value)?, write!(f, "[")?, "]")
}
ServerEnd(_, ty) => write!(f, "ServerEnd<{ty}>"),
ClientEnd(_, ty) => write!(f, "ClientEnd<{ty}>"),
Handle(_, ty) => write!(f, "<{:?}>", ty),
OutOfLine(t) => write!(f, "{t}"),
}
}
}
/// Helper macro for constructing `Value` objects. Object body is similar to a Rust struct body, and
/// field values are automatically converted to `Value` via the `From` trait.
#[macro_export]
macro_rules! vobject {
($name:ident $($tokens:tt)*) => {{
vobject!((stringify!($name)) $($tokens)*)
}};
(($name:expr) $($tokens:tt)*) => {{
#[allow(unused_mut)]
let mut items = Vec::<(String, $crate::value::Value)>::new();
vobject!(~private items | ($name) $($tokens)*);
$crate::value::Value::Struct(items)
}};
() => {{ $crate::value::Value::Struct(Vec::new()) }};
(~private $items:path | $name:ident $($tokens:tt)*) => {
vobject!(~private $items | (stringify!($name)) $($tokens)*);
};
(~private $items:path | ($name:expr) : $value:expr , $($excess:tt)*) => {
$items.push(($name.to_string(), $crate::value::Value::from($value)));
vobject!(~private $items | $($excess)*);
};
(~private $items:path | ($name:expr) : $value:expr) => {
vobject!(~private $items | ($name) : $value ,);
};
(~private $items:path |) => {};
}
/// Helper macro for constructing `Value::Union` values.
#[macro_export]
macro_rules! vunion {
($name:expr, $variant:expr, $value:expr) => {{
$crate::value::Value::Union($name.to_owned(), $variant.to_owned(), Box::new($value.into()))
}};
}