intern_str_codegen/
lib.rs

1//! Convert a `Graph` into its Rust code equivalent.
2//!
3//! This allows one to generate a `Graph` at build time and then insert it
4//! into the binary. This allows large graphs to be used in embedded systems
5//! with next to no cost.
6//!
7//! ## Example
8//!
9//! ```no_run
10//! use intern_str::{Graph, Segmentable};
11//! use intern_str::builder::{Builder, Utf8Graph};
12//! use intern_str_codegen::generate;
13//! use std::{fs::File, io::{prelude::*, BufWriter}};
14//!
15//! # fn main() -> std::io::Result<()> {
16//! let mut builder = Builder::<_, Utf8Graph>::new();
17//!
18//! builder.add("hello", 1).unwrap();
19//! builder.add("world", 2).unwrap();
20//!
21//! let mut buffer = Vec::new();
22//! let graph = builder.build(&mut buffer);
23//!
24//! // Convert to string.
25//! let code = generate(
26//!     &graph,
27//!     "&'static str",
28//!     "usize",
29//!     |f, out| write!(f, "{}", out),
30//! );
31//!
32//! let mut out = BufWriter::new(File::create("graph.rs").unwrap());
33//! writeln!(
34//!     out,
35//!     "const GRAPH: intern_str::Graph<'static, 'static, &'static str, usize> = {}",
36//!     code,
37//! )?;
38//! # Ok(()) }
39//! ```
40
41#![no_std]
42#![forbid(
43    unsafe_code,
44    missing_docs,
45    missing_debug_implementations,
46    missing_copy_implementations,
47    trivial_casts,
48    trivial_numeric_casts,
49    unused_import_braces,
50    unused_qualifications,
51    future_incompatible,
52    rust_2018_idioms
53)]
54
55extern crate alloc;
56
57use alloc::string::String;
58use core::fmt::{self, Write};
59use core::{write, writeln};
60
61use intern_str::{CaseInsensitive, Graph, Segmentable};
62
63/// The whole point.
64///
65/// See the crate documentation for more information.
66pub fn generate<Input: Key, Output>(
67    graph: &Graph<'_, '_, Input, Output>,
68    input_type: &str,
69    output_type: &str,
70    mut write_output: impl FnMut(&mut dyn Write, &Output) -> fmt::Result,
71) -> String {
72    let mut out = String::new();
73
74    writeln!(out, "{{").ok();
75
76    // Write the nodes.
77    writeln!(
78        out,
79        "{}const NODES: &[intern_str::Node<'static, {}, {}>] = &[",
80        Indent(4),
81        input_type,
82        output_type
83    )
84    .ok();
85
86    for node in graph.nodes().iter() {
87        writeln!(out, "{}intern_str::Node::new(", Indent(8)).ok();
88
89        writeln!(out, "{}&[", Indent(12)).ok();
90
91        for (input, next) in node.inputs() {
92            writeln!(
93                out,
94                "{}({}, {}),",
95                Indent(16),
96                WriteKey(input),
97                next
98            )
99            .ok();
100        }
101
102        writeln!(out, "{}],", Indent(12)).ok();
103
104        write!(out, "{}", Indent(12)).ok();
105        write_output(&mut out, node.output()).ok();
106        writeln!(out, ",").ok();
107
108        writeln!(out, "{}{},", Indent(12), node.default(),).ok();
109
110        writeln!(out, "{}{},", Indent(12), Index(node.amount()),).ok();
111
112        writeln!(out, "{}),", Indent(8)).ok();
113    }
114
115    writeln!(out, "{}];", Indent(4)).ok();
116
117    // Write the graph.
118    writeln!(
119        out,
120        "{}const GRAPH: intern_str::Graph<'static, 'static, {}, {}> = intern_str::Graph::new(NODES, {});",
121        Indent(4),
122        input_type,
123        output_type,
124        graph.start(),
125    ).ok();
126
127    writeln!(out, "{}GRAPH", Indent(4)).ok();
128
129    writeln!(out, "}}").ok();
130
131    out
132}
133
134/// An item that can be used as a key.
135pub trait Key: Segmentable {
136    /// Format the key as a Rust expression.
137    fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
138}
139
140impl<'a> Key for &'a str {
141    fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        write!(f, "\"{}\"", self)
143    }
144}
145
146impl<'a, T: fmt::Debug + Ord> Key for &'a [T] {
147    fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        write!(f, "&[")?;
149
150        for (i, item) in self.iter().enumerate() {
151            if i != 0 {
152                write!(f, ", ")?;
153            }
154
155            write!(f, "{:?}", item)?;
156        }
157
158        write!(f, "]")
159    }
160}
161
162impl<T: AsRef<[u8]> + Key> Key for CaseInsensitive<T> {
163    fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        write!(f, "intern_str::CaseInsensitive({})", WriteKey(&self.0))
165    }
166}
167
168struct WriteKey<'a, T>(&'a T);
169
170impl<'a, T: Key> fmt::Display for WriteKey<'a, T> {
171    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172        self.0.format(f)
173    }
174}
175
176struct Indent(usize);
177
178impl fmt::Display for Indent {
179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180        for _ in 0..self.0 {
181            write!(f, " ")?;
182        }
183
184        Ok(())
185    }
186}
187
188struct Index(usize);
189
190impl fmt::Display for Index {
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        if self.0 == core::usize::MAX {
193            f.write_str("core::usize::MAX")
194        } else {
195            fmt::Display::fmt(&self.0, f)
196        }
197    }
198}