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
// ┌───────────────────────────────────────────────────────────────────────────┐
// │                                                                           │
// │  ██████╗ ██████╗  ██████╗   Copyright (C) 2022, The Prospective Company   │
// │  ██╔══██╗██╔══██╗██╔═══██╗                                                │
// │  ██████╔╝██████╔╝██║   ██║  This file is part of the Procss library,      │
// │  ██╔═══╝ ██╔══██╗██║   ██║  distributed under the terms of the            │
// │  ██║     ██║  ██║╚██████╔╝  Apache License 2.0.  The full license can     │
// │  ╚═╝     ╚═╝  ╚═╝ ╚═════╝   be found in the LICENSE file.                 │
// │                                                                           │
// └───────────────────────────────────────────────────────────────────────────┘

use std::path::PathBuf;

use crate::render::RenderCss;

/// A wrapper around [`Vec`] which guarantees at least `N` elements.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct MinVec<T, const N: usize>([T; N], Vec<T>);

impl<T, const N: usize> MinVec<T, N>
where
    T: std::fmt::Debug,
{
    /// Create a new N-element-guaranteed collection.
    pub fn new(head: [T; N], tail: Vec<T>) -> Self {
        MinVec(head, tail)
    }

    /// Iterate over the values in this collection.
    pub fn iter(&self) -> impl Iterator<Item = &'_ T> {
        self.0.iter().chain(self.1.iter())
    }

    /// Iterate over the values in this collection.
    pub fn iter_mut(&mut self) -> impl Iterator<Item = &'_ mut T> {
        self.0.iter_mut().chain(self.1.iter_mut())
    }
}

impl<T: RenderCss, const N: usize> RenderCss for MinVec<T, N> {
    fn render(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for x in self.0.iter() {
            x.render(f)?;
        }

        for x in self.1.iter() {
            write!(f, ",")?;
            x.render(f)?;
        }

        Ok(())
    }
}

/// Givens a root path `outdir` and a relative path `path`, remove the extension
/// to the latter and join with the former.  If the latter path is not relative,
/// return the former.  Useful for moving directory trees while retaining their
/// relative structure to some root.
pub fn join_paths(outdir: &str, path: &str) -> PathBuf {
    if let Some(parent) = PathBuf::from(path).parent() {
        PathBuf::from(outdir).join(parent)
    } else {
        PathBuf::from(outdir)
    }
}

#[cfg(feature = "iotest")]
mod mock {
    #[mockall::automock]
    pub trait IoTestFs {
        fn read_to_string(path: &std::path::Path) -> std::io::Result<String>;
        // where
        //     P: AsRef<std::path::Path> + 'static;

        fn create_dir_all<P>(path: P) -> std::io::Result<()>
        where
            P: AsRef<std::path::Path> + 'static;

        fn write<P, C>(path: P, content: C) -> std::io::Result<()>
        where
            P: AsRef<std::path::Path> + 'static,
            C: AsRef<[u8]> + 'static;
    }
}

#[cfg(not(feature = "iotest"))]
pub use std::fs;

#[cfg(feature = "iotest")]
pub use mock::{IoTestFs, MockIoTestFs as fs};