use crate::{
types::{
Account, Date, Decimal, Duration, Float32, Float64, IntBig, NatBig, Principal, Subaccount,
Timestamp, U256, Ulid,
},
value::PublicValue,
};
use candid::{CandidType, types::Serializer};
use serde::{Deserialize, Deserializer};
#[cfg(test)]
use crate::value::Value;
#[repr(transparent)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct OutputValue(PublicValue);
impl OutputValue {
#[must_use]
pub const fn as_public(&self) -> &PublicValue {
&self.0
}
#[must_use]
pub fn into_public(self) -> PublicValue {
self.0
}
pub(crate) const fn from_public(value: PublicValue) -> Self {
Self(value)
}
#[must_use]
pub const fn account(value: Account) -> Self {
Self(PublicValue::Account(value))
}
#[must_use]
pub const fn blob(value: Vec<u8>) -> Self {
Self(PublicValue::Blob(value))
}
#[must_use]
pub const fn boolean(value: bool) -> Self {
Self(PublicValue::Bool(value))
}
#[must_use]
pub const fn date(value: Date) -> Self {
Self(PublicValue::Date(value))
}
#[must_use]
pub const fn decimal(value: Decimal) -> Self {
Self(PublicValue::Decimal(value))
}
#[must_use]
pub const fn duration(value: Duration) -> Self {
Self(PublicValue::Duration(value))
}
#[must_use]
pub const fn float32(value: Float32) -> Self {
Self(PublicValue::Float32(value))
}
#[must_use]
pub const fn float64(value: Float64) -> Self {
Self(PublicValue::Float64(value))
}
#[must_use]
pub const fn int64(value: i64) -> Self {
Self(PublicValue::Int64(value))
}
#[must_use]
pub const fn int128(value: i128) -> Self {
Self(PublicValue::Int128(value))
}
#[must_use]
pub const fn int_big(value: IntBig) -> Self {
Self(PublicValue::IntBig(value))
}
#[must_use]
pub const fn list(values: Vec<PublicValue>) -> Self {
Self(PublicValue::List(values))
}
#[must_use]
pub const fn map(entries: Vec<(PublicValue, PublicValue)>) -> Self {
Self(PublicValue::Map(entries))
}
#[must_use]
pub const fn null() -> Self {
Self(PublicValue::Null)
}
#[must_use]
pub const fn principal(value: Principal) -> Self {
Self(PublicValue::Principal(value))
}
#[must_use]
pub const fn subaccount(value: Subaccount) -> Self {
Self(PublicValue::Subaccount(value))
}
#[must_use]
pub const fn text(value: String) -> Self {
Self(PublicValue::Text(value))
}
#[must_use]
pub const fn timestamp(value: Timestamp) -> Self {
Self(PublicValue::Timestamp(value))
}
#[must_use]
pub const fn nat64(value: u64) -> Self {
Self(PublicValue::Nat64(value))
}
#[must_use]
pub const fn nat128(value: u128) -> Self {
Self(PublicValue::Nat128(value))
}
#[must_use]
pub const fn nat_big(value: NatBig) -> Self {
Self(PublicValue::NatBig(value))
}
#[must_use]
pub const fn ulid(value: Ulid) -> Self {
Self(PublicValue::Ulid(value))
}
#[must_use]
pub const fn unit() -> Self {
Self(PublicValue::Unit)
}
#[must_use]
pub const fn u256(value: U256) -> Self {
Self(PublicValue::U256(value))
}
}
impl CandidType for OutputValue {
fn ty() -> candid::types::Type {
PublicValue::ty()
}
fn _ty() -> candid::types::Type {
PublicValue::_ty()
}
fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
where
S: Serializer,
{
self.0.idl_serialize(serializer)
}
}
impl<'de> Deserialize<'de> for OutputValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
PublicValue::deserialize(deserializer).map(Self)
}
}
#[cfg(test)]
impl From<Value> for OutputValue {
fn from(value: Value) -> Self {
Self(
PublicValue::try_from_runtime_non_enum(&value)
.expect("test output conversion requires accepted enum catalog"),
)
}
}
#[must_use]
pub fn render_output_value_text(value: &OutputValue) -> String {
value.as_public().render_text()
}
#[cfg(test)]
mod tests {
use crate::value::{OutputValue, PublicValue, Value};
#[test]
fn output_value_from_runtime_value_keeps_recursive_collection_shape() {
let runtime = Value::List(vec![
Value::Nat64(7),
Value::Map(vec![(Value::Text("x".to_string()), Value::Bool(true))]),
]);
assert_eq!(
OutputValue::from(runtime),
OutputValue::list(vec![
PublicValue::Nat64(7),
PublicValue::Map(vec![(
PublicValue::Text("x".to_string()),
PublicValue::Bool(true),
)]),
]),
);
}
}