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
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//! Support for the codegen module.
#![doc(hidden)]

use std::mem::{size_of, zeroed};
use std::fmt::*;

/// Implementors correspond to formatting traits which may apply to values.
pub trait FormatTrait {
    /// Return whether this format trait is applicable to a type.
    #[inline]
    fn allowed<T>() -> bool;
    /// Format a value of the given trait using this format trait.
    /// Must panic if `allowed::<T>()` is false.
    #[inline]
    fn perform<T>(t: &T, f: &mut Formatter) -> Result;
}

// Abuse specialization to provide the `FormatTrait` impl for the actual
// format traits without requiring HKT or other deep chicanery.
trait Specialized<T> {
    #[inline]
    fn allowed() -> bool;
    #[inline]
    fn perform(t: &T, f: &mut Formatter) -> Result;
}

macro_rules! impl_format_trait {
    ($($name:ident,)*) => {
        $(
            impl<T> Specialized<T> for $name {
                #[inline]
                default fn allowed() -> bool { false }
                #[inline]
                default fn perform(_: &T, _: &mut Formatter) -> Result {
                    panic!()
                }
            }

            impl<T: $name> Specialized<T> for $name {
                #[inline]
                fn allowed() -> bool { true }
                #[inline]
                fn perform(t: &T, f: &mut Formatter) -> Result {
                    t.fmt(f)
                }
            }

            impl FormatTrait for $name {
                #[inline]
                fn allowed<T>() -> bool { <Self as Specialized<T>>::allowed() }
                #[inline]
                fn perform<T>(t: &T, f: &mut Formatter) -> Result {
                    <Self as Specialized<T>>::perform(t, f)
                }
            }
        )*
    }
}

impl_format_trait! {
    Display, Debug, LowerExp, UpperExp, Octal, Pointer, Binary, LowerHex,
    UpperHex,
}

#[inline]
fn get_formatter<T, F: FormatTrait + ?Sized>()
    -> Option<impl Fn(&T, &mut Formatter) -> Result>
{
    if F::allowed::<T>() {
        Some(F::perform::<T>)
    } else {
        None
    }
}

#[inline]
// The combined function which will be returned by `make_combined`.
fn combined<A, B, LHS, RHS>(a: &A, f: &mut Formatter) -> Result
    where LHS: Fn(&A) -> &B, RHS: Fn(&B, &mut Formatter) -> Result
{
    let lhs = unsafe { zeroed::<LHS>() };
    let rhs = unsafe { zeroed::<RHS>() };
    rhs(lhs(a), f)
}

// Local type alias for the formatting function pointer type.
type FormatFn<T> = fn(&T, &mut Formatter) -> Result;

// Accepts dummy arguments to allow type parameter inference, and returns
// `combined` instantiated with those arguments.
#[inline]
fn make_combined<A, B, LHS, RHS>(_: LHS, _: RHS) -> FormatFn<A>
    where LHS: Fn(&A) -> &B, RHS: Fn(&B, &mut Formatter) -> Result
{
    // check that both function
    assert!(size_of::<LHS>() == 0,
        "Mapper from parent to child must be zero-sized, instead size was {}",
        size_of::<LHS>());
    assert!(size_of::<RHS>() == 0,
        "Formatting function must be zero-sized, instead size was {}",
        size_of::<RHS>());
    combined::<A, B, LHS, RHS>
}

/// Combine a function from `&A` to `&B` and a formatting trait applicable to
/// `B` and return a function pointer which will convert a `&A` to `&B` and
/// then format it with the given trait.
///
/// Returns `None` if the formatting trait is not applicable to `B`.
/// Panics if `func` is not zero-sized.
#[inline]
pub fn combine<F, A, B, Func>(func: Func)
    -> Option<FormatFn<A>>
    where F: FormatTrait + ?Sized, Func: Fn(&A) -> &B
{
    // Combines `get_formatter` and `make_combined` in one.
    get_formatter::<B, F>().map(|r| make_combined(func, r))
}

// Specialization abuse to select only functions which return `&usize`.
trait SpecUsize {
    #[inline]
    fn convert<T>(f: fn(&T) -> &Self) -> Option<fn(&T) -> &usize>;
}

impl<U> SpecUsize for U {
    #[inline]
    default fn convert<T>(_: fn(&T) -> &Self) -> Option<fn(&T) -> &usize> { None }
}

impl SpecUsize for usize {
    #[inline]
    fn convert<T>(f: fn(&T) -> &usize) -> Option<fn(&T) -> &usize> { Some(f) }
}

/// Attempt to convert a function from `&A` to `&B` to a function from `&A`
/// to `&usize`. Returns `Some` only when `B` is `usize`.
#[inline]
pub fn as_usize<A, B>(f: fn(&A) -> &B) -> Option<fn(&A) -> &usize> {
    <B as SpecUsize>::convert::<A>(f)
}

/// A trait for types against which formatting specifiers may be pre-checked.
///
/// Implementations may be generated automatically using `runtime-fmt-derive`
/// and `#[derive(FormatArgs)]`.
pub trait FormatArgs {
    /// Find the index within this type corresponding to the provided name.
    ///
    /// If this function returns `Some`, `get_child` with the returned index
    /// must not panic.
    fn validate_name(name: &str) -> Option<usize>;

    /// Validate that a given index is within range for this type.
    ///
    /// If this function returns `true`, `get_child` with the given index must
    /// not panic.
    fn validate_index(index: usize) -> bool;

    /// Return the formatter function for the given format trait, accepting
    /// `&Self` and using the given format trait on the value at that index.
    ///
    /// Returns `None` if the given format trait cannot format the child at
    /// that index. Panics if the index is invalid.
    fn get_child<F: FormatTrait + ?Sized>(index: usize) -> Option<FormatFn<Self>>;

    /// Return the value at the given index interpreted as a `usize`.
    ///
    /// Returns `None` if the child at the given index cannot be interpreted
    /// as a `usize`. Panics if the index is invalid.
    fn as_usize(index: usize) -> Option<fn(&Self) -> &usize>;
}