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
use crate::FileSystem;

/// A chain of one or more [`FileSystem`]s.
pub trait FileSystems<'a>: 'a {
    // FIXME(Michael-F-Bryan): Rewrite this to use GATs when we bump the MSRV to
    // 1.65 or higher. That'll get rid of all the lifetimes and HRTBs.
    type Iter: IntoIterator<Item = &'a dyn FileSystem> + 'a;

    /// Get something that can be used to iterate over the underlying
    /// filesystems.
    fn filesystems(&'a self) -> Self::Iter;
}

impl<'a, 'b, S> FileSystems<'a> for &'b S
where
    S: FileSystems<'a> + 'b,
    'b: 'a,
{
    type Iter = S::Iter;

    fn filesystems(&'a self) -> Self::Iter {
        (**self).filesystems()
    }
}

impl<'a, T> FileSystems<'a> for Vec<T>
where
    T: FileSystem,
{
    type Iter = <[T] as FileSystems<'a>>::Iter;

    fn filesystems(&'a self) -> Self::Iter {
        self[..].filesystems()
    }
}

impl<'a, T, const N: usize> FileSystems<'a> for [T; N]
where
    T: FileSystem,
{
    type Iter = [&'a dyn FileSystem; N];

    fn filesystems(&'a self) -> Self::Iter {
        // TODO: rewrite this when array::each_ref() is stable
        let mut i = 0;
        [(); N].map(|_| {
            let f = &self[i] as &dyn FileSystem;
            i += 1;
            f
        })
    }
}

impl<'a, T> FileSystems<'a> for [T]
where
    T: FileSystem,
{
    type Iter = std::iter::Map<std::slice::Iter<'a, T>, fn(&T) -> &dyn FileSystem>;

    fn filesystems(&'a self) -> Self::Iter {
        self.iter().map(|fs| fs as &dyn FileSystem)
    }
}

impl<'a> FileSystems<'a> for () {
    type Iter = std::iter::Empty<&'a dyn FileSystem>;

    fn filesystems(&'a self) -> Self::Iter {
        std::iter::empty()
    }
}

macro_rules! count {
    ($first:tt $($rest:tt)*) => {
        1 + count!($($rest)*)
    };
    () => { 0 };
}

macro_rules! tuple_filesystems {
    ($first:ident $(, $rest:ident)* $(,)?) => {
        impl<'a, $first, $( $rest ),*> FileSystems<'a> for ($first, $($rest),*)
        where
            $first: FileSystem,
            $($rest: FileSystem),*
        {
            type Iter = [&'a dyn FileSystem; count!($first $($rest)*)];

            fn filesystems(&'a self) -> Self::Iter {
                #[allow(non_snake_case)]
                let ($first, $($rest),*) = self;

                [
                    $first as &dyn FileSystem,
                    $($rest),*
                ]
            }

        }

        tuple_filesystems!($($rest),*);
    };
    () => {};
}

tuple_filesystems!(F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16,);