Skip to content

Commit

Permalink
✨ zv: impl Display for Value, Array, Structure, Dict & Maybe
Browse files Browse the repository at this point in the history
Closes #366
  • Loading branch information
SeaDve committed Jun 22, 2023
1 parent 8d07191 commit 88be28b
Show file tree
Hide file tree
Showing 6 changed files with 568 additions and 14 deletions.
1 change: 1 addition & 0 deletions zvariant/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ ostree-tests = ["gvariant"]

[dependencies]
byteorder = "1.4.3"
unicode-general-category = "0.6"
serde = { version = "1.0", features = ["derive"] }
arrayvec = { version = "0.7.2", features = ["serde"], optional = true }
enumflags2 = { version = "0.7.5", features = ["serde"], optional = true }
Expand Down
116 changes: 114 additions & 2 deletions zvariant/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ use serde::{
ser::{Serialize, SerializeSeq, Serializer},
};
use static_assertions::assert_impl_all;
use std::convert::{TryFrom, TryInto};
use std::{
convert::{TryFrom, TryInto},
fmt::{Display, Write},
};

use crate::{
value::SignatureSeed, DynamicDeserialize, DynamicType, Error, Result, Signature, Type, Value,
value::{value_display_fmt, SignatureSeed},
DynamicDeserialize, DynamicType, Error, Result, Signature, Type, Value,
};

/// A helper type to wrap arrays in a [`Value`].
Expand Down Expand Up @@ -101,6 +105,114 @@ impl<'a> Array<'a> {
}
}

impl Display for Array<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
array_display_fmt(self, f, true)
}
}

pub(crate) fn array_display_fmt(
array: &Array<'_>,
f: &mut std::fmt::Formatter<'_>,
type_annotate: bool,
) -> std::fmt::Result {
// Print as string if it is a bytestring (i.e., first nul character is the last byte)
if array.element_signature() == &u8::signature()
&& array
.iter()
.position(|value| value == &Value::U8(b'\0'))
.is_some_and(|position| position == array.len() - 1)
{
// Implemented based on https://gitlab.gnome.org/GNOME/glib/blob/2.66.2/glib/gstrfuncs.c#L2172
let mut escaped = Vec::with_capacity(array.len() - 1);
for item in array.iter().take(array.len() - 1) {
let Value::U8(val) = item else {
unreachable!("item must have a signature of byte");
};

match val {
0x08 => {
escaped.push(b'\\');
escaped.push(b'b');
}
b'\t' => {
escaped.push(b'\\');
escaped.push(b't');
}
b'\n' => {
escaped.push(b'\\');
escaped.push(b'n');
}
0x0b => {
escaped.push(b'\\');
escaped.push(b'v');
}
0x0c => {
escaped.push(b'\\');
escaped.push(b'f');
}
b'\r' => {
escaped.push(b'\\');
escaped.push(b'r');
}
b'\\' => {
escaped.push(b'\\');
escaped.push(b'\\');
}
b'"' => {
escaped.push(b'\\');
escaped.push(b'"');
}
byte => {
if !(b' '..0o177).contains(byte) {
escaped.push(b'\\');
escaped.push(b'0' + ((byte >> 6) & 0o7));
escaped.push(b'0' + ((byte >> 3) & 0o7));
escaped.push(b'0' + (byte & 0o7));
} else {
escaped.push(*byte);
}
}
}
}

let string = String::from_utf8_lossy(&escaped);
if string.find('\'').is_some() {
write!(f, "b\"{}\"", string)?;
} else {
write!(f, "b'{}'", string)?;
}

return Ok(());
}

if array.is_empty() {
if type_annotate {
write!(f, "@{} ", array.full_signature())?;
}
f.write_str("[]")?;
} else {
f.write_char('[')?;

// Annotate only the first item as the rest will be
// of the same type.
let mut type_annotate = type_annotate;

for (i, item) in array.iter().enumerate() {
value_display_fmt(item, f, type_annotate)?;
type_annotate = false;

if i + 1 < array.len() {
f.write_str(", ")?;
}
}

f.write_char(']')?;
}

Ok(())
}

/// Use this to deserialize an [Array].
pub struct ArraySeed<'a> {
signature: Signature<'a>,
Expand Down
49 changes: 47 additions & 2 deletions zvariant/src/dict.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use std::{collections::HashMap, convert::TryFrom, hash::BuildHasher};
use std::{
collections::HashMap,
convert::TryFrom,
fmt::{Display, Write},
hash::BuildHasher,
};

use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer};
use static_assertions::assert_impl_all;

use crate::{Basic, DynamicType, Error, Signature, Type, Value};
use crate::{value_display_fmt, Basic, DynamicType, Error, Signature, Type, Value};

/// A helper type to wrap dictionaries in a [`Value`].
///
Expand Down Expand Up @@ -137,6 +142,46 @@ impl<'k, 'v> Dict<'k, 'v> {
// TODO: Provide more API like https://docs.rs/toml/0.5.5/toml/map/struct.Map.html
}

impl Display for Dict<'_, '_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
dict_display_fmt(self, f, true)
}
}

pub(crate) fn dict_display_fmt(
dict: &Dict<'_, '_>,
f: &mut std::fmt::Formatter<'_>,
type_annotate: bool,
) -> std::fmt::Result {
if dict.entries.is_empty() {
if type_annotate {
write!(f, "@{} ", dict.full_signature())?;
}
f.write_str("{}")?;
} else {
f.write_char('{')?;

// Annotate only the first entry as the rest will be
// of the same type.
let mut type_annotate = type_annotate;

for (i, entry) in dict.entries.iter().enumerate() {
value_display_fmt(&entry.key, f, type_annotate)?;
f.write_str(": ")?;
value_display_fmt(&entry.value, f, type_annotate)?;
type_annotate = false;

if i + 1 < dict.entries.len() {
f.write_str(", ")?;
}
}

f.write_char('}')?;
}

Ok(())
}

impl<'k, 'v> Serialize for Dict<'k, 'v> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
Expand Down
47 changes: 45 additions & 2 deletions zvariant/src/maybe.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use serde::ser::{Serialize, Serializer};
use static_assertions::assert_impl_all;
use std::convert::TryFrom;
use std::{convert::TryFrom, fmt::Display};

use crate::{Error, Signature, Type, Value};
use crate::{value_display_fmt, Error, Signature, Type, Value};

/// A helper type to wrap `Option<T>` (GVariant's Maybe type) in [`Value`].
///
Expand Down Expand Up @@ -103,6 +103,49 @@ impl<'a> Maybe<'a> {
}
}

impl Display for Maybe<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
maybe_display_fmt(self, f, true)
}
}

pub(crate) fn maybe_display_fmt(
maybe: &Maybe<'_>,
f: &mut std::fmt::Formatter<'_>,
type_annotate: bool,
) -> std::fmt::Result {
if type_annotate {
write!(f, "@{} ", maybe.full_signature())?;
}

let (last_inner, depth) = {
let mut curr = maybe.inner();
let mut depth = 0;

while let Some(Value::Maybe(child_maybe)) = curr {
curr = child_maybe.inner();
depth += 1;
}

(curr, depth)
};

if let Some(last_inner) = last_inner {
// There are no Nothings, so print out the inner
// value with no prefixes.
value_display_fmt(last_inner, f, false)?;
} else {
// One of the maybes was Nothing, so print out the
// right number of justs.
for _ in 0..depth {
f.write_str("just ")?;
}
f.write_str("nothing")?;
}

Ok(())
}

impl<'a, T> From<Option<T>> for Maybe<'a>
where
T: Type + Into<Value<'a>>,
Expand Down
44 changes: 41 additions & 3 deletions zvariant/src/structure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ use serde::{
ser::{Serialize, SerializeTupleStruct, Serializer},
};
use static_assertions::assert_impl_all;
use std::convert::TryInto;
use std::{
convert::TryInto,
fmt::{Display, Write},
};

use crate::{
signature_parser::SignatureParser, value::SignatureSeed, DynamicDeserialize, DynamicType,
OwnedValue, Signature, Value,
signature_parser::SignatureParser, value::SignatureSeed, value_display_fmt, DynamicDeserialize,
DynamicType, OwnedValue, Signature, Value,
};

/// Use this to efficiently build a [`Structure`].
Expand Down Expand Up @@ -197,6 +200,41 @@ impl<'a> Structure<'a> {
}
}

impl Display for Structure<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
structure_display_fmt(self, f, true)
}
}

pub(crate) fn structure_display_fmt(
structure: &Structure<'_>,
f: &mut std::fmt::Formatter<'_>,
type_annotate: bool,
) -> std::fmt::Result {
f.write_char('(')?;

let fields = structure.fields();

match fields.len() {
0 => {}
1 => {
value_display_fmt(&fields[0], f, type_annotate)?;
f.write_char(',')?;
}
_ => {
for (i, field) in fields.iter().enumerate() {
value_display_fmt(field, f, type_annotate)?;

if i + 1 < fields.len() {
f.write_str(", ")?;
}
}
}
}

f.write_char(')')
}

impl<'a> Default for Structure<'a> {
fn default() -> Self {
let signature = Signature::from_static_str_unchecked("()");
Expand Down

0 comments on commit 88be28b

Please sign in to comment.