ffizz_header/
lib.rs

1#![doc = include_str!("crate-doc.md")]
2
3use itertools::join;
4use linkme::distributed_slice;
5use std::cmp::Ordering;
6
7#[doc(hidden)]
8pub use linkme;
9
10pub use ffizz_macros::item;
11pub use ffizz_macros::snippet;
12
13/// A HeaderItem contains an item that should be included in the output C header.
14///
15/// Only the `content` field will actually appear, with the other fields used to ensure a stable
16/// order for the items.  `order` is used for coarse-grained ordering, such as putting introductory
17/// comments at the top.  For items with equal `order`, `name` is used to sort.
18#[doc(hidden)]
19#[derive(Clone)]
20pub struct HeaderItem {
21    pub order: usize,
22    pub name: &'static str,
23    pub content: &'static str,
24}
25
26/// FFIZZ_HEADER_ITEMS collects HeaderItems using `linkme`.
27#[doc(hidden)]
28#[distributed_slice]
29pub static FFIZZ_HEADER_ITEMS: [HeaderItem] = [..];
30
31/// Generate the C header for the library.
32///
33/// This "magically" concatenates all of the header chunks supplied by `item` and `snippet` macro
34/// invocations throughout all crates used to build the library.
35pub fn generate() -> String {
36    generate_from_vec(FFIZZ_HEADER_ITEMS.iter().collect::<Vec<_>>())
37}
38
39/// Inner version of generate that does not operate on a static value.
40fn generate_from_vec(mut items: Vec<&'static HeaderItem>) -> String {
41    items.sort_by(
42        |a: &&'static HeaderItem, b: &&'static HeaderItem| match a.order.cmp(&b.order) {
43            Ordering::Less => Ordering::Less,
44            Ordering::Equal => a.name.cmp(b.name),
45            Ordering::Greater => Ordering::Greater,
46        },
47    );
48
49    // join the items with blank lines
50    let mut result = join(items.iter().map(|hi| hi.content.trim()), "\n\n");
51    // and ensure a trailing newline
52    if !items.is_empty() {
53        result.push('\n');
54    }
55    result
56}
57
58#[cfg(test)]
59mod test {
60    #[test]
61    fn test_generate_order_by_order() {
62        assert_eq!(
63            super::generate_from_vec(vec![
64                &super::HeaderItem {
65                    order: 1,
66                    name: "foo",
67                    content: "one"
68                },
69                &super::HeaderItem {
70                    order: 3,
71                    name: "foo",
72                    content: "three"
73                },
74                &super::HeaderItem {
75                    order: 2,
76                    name: "foo",
77                    content: "two"
78                },
79            ]),
80            String::from("one\n\ntwo\n\nthree\n")
81        );
82    }
83
84    #[test]
85    fn test_generate_order_by_name() {
86        assert_eq!(
87            super::generate_from_vec(vec![
88                &super::HeaderItem {
89                    order: 3,
90                    name: "bbb",
91                    content: "two"
92                },
93                &super::HeaderItem {
94                    order: 3,
95                    name: "ccc",
96                    content: "three"
97                },
98                &super::HeaderItem {
99                    order: 3,
100                    name: "aaa",
101                    content: "one"
102                },
103            ]),
104            String::from("one\n\ntwo\n\nthree\n")
105        );
106    }
107
108    #[test]
109    fn test_empty() {
110        assert_eq!(super::generate(), String::new());
111    }
112}