1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/master/LICENSE ).
//! `writeable` is a utility crate of the [`ICU4X`] project.
//!
//! It includes [`Writeable`], a core trait representing an object that can be written to a
//! sink implementing std::fmt::Write. It is an alternative to std::fmt::Display with the
//! addition of a function indicating the number of bytes to be written.
//!
//! Writeable improves upon std::fmt::Display in two ways:
//!
//! 1. More efficient, since the sink can pre-allocate bytes.
//! 2. Smaller code, since the format machinery can be short-circuited.
//!
//! Types implementing Writeable automatically implement ToString. Because of this, you cannot
//! implement both Writeable and std::fmt::Display on the same type.
//!
//! [`Writeable`]: ./trait.Writeable.html
//! [`ICU4X`]: ../icu/index.html

use std::fmt;

/// Writeable is an alternative to std::fmt::Display with the addition of a length function.
pub trait Writeable {
    /// Writes bytes to the given sink. Errors from the sink are bubbled up.
    fn write_to(&self, sink: &mut dyn fmt::Write) -> fmt::Result;

    /// Returns an estimation of the number of bytes that will be written to the sink. The actual
    /// number of bytes may be slightly different than what this function returns.
    ///
    /// This function may return an enumeration in the future. See:
    /// https://github.com/unicode-org/icu4x/issues/370
    fn write_len(&self) -> usize;

    /// Creates a new String with the data from this Writeable. Like ToString, but faster.
    fn writeable_to_string(&self) -> String {
        let mut output = String::with_capacity(self.write_len());
        self.write_to(&mut output)
            .expect("impl Write for String is infallible");
        output
    }
}

/// Testing macro for types implementing Writeable. The first argument should be a string, and
/// the second argument should be a `&dyn Writeable`.
///
/// The macro tests for equality of both string content and string length. If your Writeable
/// implementation returns an inexact string length, don't use this macro.
///
/// # Example
///
/// ```
/// use writeable::Writeable;
/// use writeable::assert_writeable_eq;
/// use std::fmt;
///
/// struct Demo;
/// impl Writeable for Demo {
///     fn write_to(&self, sink: &mut dyn fmt::Write) -> fmt::Result {
///         sink.write_str("foo")
///     }
///     fn write_len(&self) -> usize {
///         3
///     }
/// }
///
/// assert_writeable_eq!("foo", &Demo);
/// ```
#[macro_export]
macro_rules! assert_writeable_eq {
    ($expected_str:expr, $actual_writeable:expr) => {
        let writeable: &dyn Writeable = $actual_writeable;
        assert_eq!($expected_str, writeable.writeable_to_string());
        assert_eq!($expected_str.len(), writeable.write_len());
    };
}