use super::string_helpers;
use crate::value::{BitSequence, Composite, Primitive, Value, ValueDef, Variant};
use std::fmt::{Display, Write};
impl<T> Display for Value<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)
}
}
impl<T> Display for ValueDef<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ValueDef::Composite(c) => c.fmt(f),
ValueDef::Variant(v) => v.fmt(f),
ValueDef::BitSequence(b) => fmt_bitsequence(b, f),
ValueDef::Primitive(p) => p.fmt(f),
}
}
}
impl<T> Display for Composite<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Composite::Named(vals) => {
f.write_str("{ ")?;
for (idx, (name, val)) in vals.iter().enumerate() {
if idx != 0 {
f.write_str(", ")?;
}
if is_ident(name) {
f.write_str(name)?;
} else {
fmt_string(name, f)?;
}
f.write_str(": ")?;
val.fmt(f)?;
}
f.write_str(" }")?;
}
Composite::Unnamed(vals) => {
f.write_char('(')?;
for (idx, val) in vals.iter().enumerate() {
if idx != 0 {
f.write_str(", ")?;
}
val.fmt(f)?;
}
f.write_char(')')?;
}
}
Ok(())
}
}
impl<T> Display for Variant<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if is_ident(&self.name) {
f.write_str(&self.name)?;
} else {
f.write_char('v')?;
fmt_string(&self.name, f)?;
}
self.values.fmt(f)
}
}
impl Display for Primitive {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Primitive::Bool(true) => f.write_str("true"),
Primitive::Bool(false) => f.write_str("false"),
Primitive::Char(c) => fmt_char(*c, f),
Primitive::I128(n) => n.fmt(f),
Primitive::U128(n) => n.fmt(f),
Primitive::String(s) => fmt_string(s, f),
Primitive::U256(_) | Primitive::I256(_) => Err(std::fmt::Error),
}
}
}
fn fmt_string(s: &str, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_char('"')?;
for char in s.chars() {
match string_helpers::to_escape_code(char) {
Some(escaped) => {
f.write_char('\\')?;
f.write_char(escaped)?
}
None => f.write_char(char)?,
}
}
f.write_char('"')
}
fn fmt_char(c: char, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_char('\'')?;
match string_helpers::to_escape_code(c) {
Some(escaped) => {
f.write_char('\\')?;
f.write_char(escaped)?
}
None => f.write_char(c)?,
}
f.write_char('\'')
}
fn fmt_bitsequence(b: &BitSequence, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_char('<')?;
for bit in b.iter() {
match bit {
true => f.write_char('1')?,
false => f.write_char('0')?,
}
}
f.write_char('>')
}
fn is_ident(s: &str) -> bool {
for (idx, c) in s.chars().enumerate() {
if (idx == 0 && !c.is_alphabetic()) || !c.is_alphanumeric() || c != '_' {
return false;
}
}
true
}
#[cfg(test)]
#[cfg(feature = "from_string")]
mod test {
use super::*;
fn assert_from_to<T: std::fmt::Debug + PartialEq>(val: Value<T>) {
let s = val.to_string();
match crate::stringify::from_str(&s) {
(Err(e), _) => {
panic!("'{s}' cannot be parsed back into the value {val:?}: {e}");
}
(Ok(new_val), rest) => {
assert_eq!(
val.remove_context(),
new_val,
"value should be the same after parsing to/from a string"
);
assert_eq!(rest.len(), 0, "there should be no unparsed string but got '{rest}'");
}
}
}
#[test]
fn primitives() {
assert_from_to(Value::bool(true));
assert_from_to(Value::bool(false));
assert_from_to(Value::char('\n'));
assert_from_to(Value::char('😀'));
assert_from_to(Value::char('a'));
assert_from_to(Value::char('\0'));
assert_from_to(Value::char('\t'));
assert_from_to(Value::i128(-123_456));
assert_from_to(Value::u128(0));
assert_from_to(Value::u128(123456));
assert_from_to(Value::string("hello \"you\",\n\n\t How are you??"));
assert_from_to(Value::string(""));
}
#[test]
fn composites() {
assert_from_to(Value::named_composite([
("foo", Value::u128(12345)),
("bar", Value::bool(true)),
("a \"weird\" name", Value::string("Woop!")),
]));
assert_from_to(Value::unnamed_composite([
Value::u128(12345),
Value::bool(true),
Value::string("Woop!"),
]));
}
#[test]
fn variants() {
assert_from_to(Value::named_variant(
"A weird variant name",
[
("foo", Value::u128(12345)),
("bar", Value::bool(true)),
("a \"weird\" name", Value::string("Woop!")),
],
));
assert_from_to(Value::unnamed_variant(
"MyVariant",
[Value::u128(12345), Value::bool(true), Value::string("Woop!")],
));
}
#[test]
fn bit_sequences() {
use scale_bits::bits;
assert_from_to(Value::bit_sequence(bits![0, 1, 1, 0, 1, 1, 0]));
assert_from_to(Value::bit_sequence(bits![]));
}
}