1 | //! A set of builders to generate Rust source for PHF data structures at |
2 | //! compile time. |
3 | //! |
4 | //! The provided builders are intended to be used in a Cargo build script to |
5 | //! generate a Rust source file that will be included in a library at build |
6 | //! time. |
7 | //! |
8 | //! # Examples |
9 | //! |
10 | //! build.rs: |
11 | //! |
12 | //! ```rust,no_run |
13 | //! use std::env; |
14 | //! use std::fs::File; |
15 | //! use std::io::{BufWriter, Write}; |
16 | //! use std::path::Path; |
17 | //! |
18 | //! fn main() { |
19 | //! let path = Path::new(&env::var("OUT_DIR" ).unwrap()).join("codegen.rs" ); |
20 | //! let mut file = BufWriter::new(File::create(&path).unwrap()); |
21 | //! |
22 | //! writeln!( |
23 | //! &mut file, |
24 | //! "static KEYWORDS: phf::Map<&'static str, Keyword> = \n{}; \n" , |
25 | //! phf_codegen::Map::new() |
26 | //! .entry("loop" , "Keyword::Loop" ) |
27 | //! .entry("continue" , "Keyword::Continue" ) |
28 | //! .entry("break" , "Keyword::Break" ) |
29 | //! .entry("fn" , "Keyword::Fn" ) |
30 | //! .entry("extern" , "Keyword::Extern" ) |
31 | //! .build() |
32 | //! ).unwrap(); |
33 | //! } |
34 | //! ``` |
35 | //! |
36 | //! lib.rs: |
37 | //! |
38 | //! ```ignore |
39 | //! #[derive(Clone)] |
40 | //! enum Keyword { |
41 | //! Loop, |
42 | //! Continue, |
43 | //! Break, |
44 | //! Fn, |
45 | //! Extern, |
46 | //! } |
47 | //! |
48 | //! include!(concat!(env!("OUT_DIR" ), "/codegen.rs" )); |
49 | //! |
50 | //! pub fn parse_keyword(keyword: &str) -> Option<Keyword> { |
51 | //! KEYWORDS.get(keyword).cloned() |
52 | //! } |
53 | //! ``` |
54 | //! |
55 | //! ##### Byte-String Keys |
56 | //! Byte strings by default produce references to fixed-size arrays; the compiler needs a hint |
57 | //! to coerce them to slices: |
58 | //! |
59 | //! build.rs: |
60 | //! |
61 | //! ```rust,no_run |
62 | //! use std::env; |
63 | //! use std::fs::File; |
64 | //! use std::io::{BufWriter, Write}; |
65 | //! use std::path::Path; |
66 | //! |
67 | //! fn main() { |
68 | //! let path = Path::new(&env::var("OUT_DIR" ).unwrap()).join("codegen.rs" ); |
69 | //! let mut file = BufWriter::new(File::create(&path).unwrap()); |
70 | //! |
71 | //! writeln!( |
72 | //! &mut file, |
73 | //! "static KEYWORDS: phf::Map<&'static [u8], Keyword> = \n{}; \n" , |
74 | //! phf_codegen::Map::<&[u8]>::new() |
75 | //! .entry(b"loop" , "Keyword::Loop" ) |
76 | //! .entry(b"continue" , "Keyword::Continue" ) |
77 | //! .entry(b"break" , "Keyword::Break" ) |
78 | //! .entry(b"fn" , "Keyword::Fn" ) |
79 | //! .entry(b"extern" , "Keyword::Extern" ) |
80 | //! .build() |
81 | //! ).unwrap(); |
82 | //! } |
83 | //! ``` |
84 | //! |
85 | //! lib.rs: |
86 | //! |
87 | //! ```rust,ignore |
88 | //! #[derive(Clone)] |
89 | //! enum Keyword { |
90 | //! Loop, |
91 | //! Continue, |
92 | //! Break, |
93 | //! Fn, |
94 | //! Extern, |
95 | //! } |
96 | //! |
97 | //! include!(concat!(env!("OUT_DIR" ), "/codegen.rs" )); |
98 | //! |
99 | //! pub fn parse_keyword(keyword: &[u8]) -> Option<Keyword> { |
100 | //! KEYWORDS.get(keyword).cloned() |
101 | //! } |
102 | //! ``` |
103 | //! |
104 | //! # Note |
105 | //! |
106 | //! The compiler's stack will overflow when processing extremely long method |
107 | //! chains (500+ calls). When generating large PHF data structures, consider |
108 | //! looping over the entries or making each call a separate statement: |
109 | //! |
110 | //! ```rust |
111 | //! let entries = [("hello" , "1" ), ("world" , "2" )]; |
112 | //! |
113 | //! let mut builder = phf_codegen::Map::new(); |
114 | //! for &(key, value) in &entries { |
115 | //! builder.entry(key, value); |
116 | //! } |
117 | //! // ... |
118 | //! ``` |
119 | //! |
120 | //! ```rust |
121 | //! let mut builder = phf_codegen::Map::new(); |
122 | //! builder.entry("hello" , "1" ); |
123 | //! builder.entry("world" , "2" ); |
124 | //! // ... |
125 | //! ``` |
126 | #![doc (html_root_url = "https://docs.rs/phf_codegen/0.9" )] |
127 | |
128 | use phf_shared::{FmtConst, PhfHash}; |
129 | use std::collections::HashSet; |
130 | use std::fmt; |
131 | use std::hash::Hash; |
132 | |
133 | use phf_generator::HashState; |
134 | |
135 | struct Delegate<T>(T); |
136 | |
137 | impl<T: FmtConst> fmt::Display for Delegate<T> { |
138 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
139 | self.0.fmt_const(f) |
140 | } |
141 | } |
142 | |
143 | /// A builder for the `phf::Map` type. |
144 | pub struct Map<K> { |
145 | keys: Vec<K>, |
146 | values: Vec<String>, |
147 | path: String, |
148 | } |
149 | |
150 | impl<K: Hash + PhfHash + Eq + FmtConst> Map<K> { |
151 | /// Creates a new `phf::Map` builder. |
152 | pub fn new() -> Map<K> { |
153 | // FIXME rust#27438 |
154 | // |
155 | // On Windows/MSVC there are major problems with the handling of dllimport. |
156 | // Here, because downstream build scripts only invoke generics from phf_codegen, |
157 | // the linker ends up throwing a way a bunch of static symbols we actually need. |
158 | // This works around the problem, assuming that all clients call `Map::new` by |
159 | // calling a non-generic function. |
160 | fn noop_fix_for_27438() {} |
161 | noop_fix_for_27438(); |
162 | |
163 | Map { |
164 | keys: vec![], |
165 | values: vec![], |
166 | path: String::from("::phf" ), |
167 | } |
168 | } |
169 | |
170 | /// Set the path to the `phf` crate from the global namespace |
171 | pub fn phf_path(&mut self, path: &str) -> &mut Map<K> { |
172 | self.path = path.to_owned(); |
173 | self |
174 | } |
175 | |
176 | /// Adds an entry to the builder. |
177 | /// |
178 | /// `value` will be written exactly as provided in the constructed source. |
179 | pub fn entry(&mut self, key: K, value: &str) -> &mut Map<K> { |
180 | self.keys.push(key); |
181 | self.values.push(value.to_owned()); |
182 | self |
183 | } |
184 | |
185 | /// Calculate the hash parameters and return a struct implementing |
186 | /// [`Display`](::std::fmt::Display) which will print the constructed `phf::Map`. |
187 | /// |
188 | /// # Panics |
189 | /// |
190 | /// Panics if there are any duplicate keys. |
191 | pub fn build(&self) -> DisplayMap<'_, K> { |
192 | let mut set = HashSet::new(); |
193 | for key in &self.keys { |
194 | if !set.insert(key) { |
195 | panic!("duplicate key ` {}`" , Delegate(key)); |
196 | } |
197 | } |
198 | |
199 | let state = phf_generator::generate_hash(&self.keys); |
200 | |
201 | DisplayMap { |
202 | path: &self.path, |
203 | keys: &self.keys, |
204 | values: &self.values, |
205 | state, |
206 | } |
207 | } |
208 | } |
209 | |
210 | /// An adapter for printing a [`Map`](Map). |
211 | pub struct DisplayMap<'a, K> { |
212 | path: &'a str, |
213 | state: HashState, |
214 | keys: &'a [K], |
215 | values: &'a [String], |
216 | } |
217 | |
218 | impl<'a, K: FmtConst + 'a> fmt::Display for DisplayMap<'a, K> { |
219 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
220 | // funky formatting here for nice output |
221 | write!( |
222 | f, |
223 | " {}::Map {{ |
224 | key: {:?}, |
225 | disps: &[" , |
226 | self.path, self.state.key |
227 | )?; |
228 | |
229 | // write map displacements |
230 | for &(d1, d2) in &self.state.disps { |
231 | write!( |
232 | f, |
233 | " |
234 | ( {}, {})," , |
235 | d1, d2 |
236 | )?; |
237 | } |
238 | |
239 | write!( |
240 | f, |
241 | " |
242 | ], |
243 | entries: &[" , |
244 | )?; |
245 | |
246 | // write map entries |
247 | for &idx in &self.state.map { |
248 | write!( |
249 | f, |
250 | " |
251 | ( {}, {})," , |
252 | Delegate(&self.keys[idx]), |
253 | &self.values[idx] |
254 | )?; |
255 | } |
256 | |
257 | write!( |
258 | f, |
259 | " |
260 | ], |
261 | }}" |
262 | ) |
263 | } |
264 | } |
265 | |
266 | /// A builder for the `phf::Set` type. |
267 | pub struct Set<T> { |
268 | map: Map<T>, |
269 | } |
270 | |
271 | impl<T: Hash + PhfHash + Eq + FmtConst> Set<T> { |
272 | /// Constructs a new `phf::Set` builder. |
273 | pub fn new() -> Set<T> { |
274 | Set { map: Map::new() } |
275 | } |
276 | |
277 | /// Set the path to the `phf` crate from the global namespace |
278 | pub fn phf_path(&mut self, path: &str) -> &mut Set<T> { |
279 | self.map.phf_path(path); |
280 | self |
281 | } |
282 | |
283 | /// Adds an entry to the builder. |
284 | pub fn entry(&mut self, entry: T) -> &mut Set<T> { |
285 | self.map.entry(entry, "()" ); |
286 | self |
287 | } |
288 | |
289 | /// Calculate the hash parameters and return a struct implementing |
290 | /// [`Display`](::std::fmt::Display) which will print the constructed `phf::Set`. |
291 | /// |
292 | /// # Panics |
293 | /// |
294 | /// Panics if there are any duplicate keys. |
295 | pub fn build(&self) -> DisplaySet<'_, T> { |
296 | DisplaySet { |
297 | inner: self.map.build(), |
298 | } |
299 | } |
300 | } |
301 | |
302 | /// An adapter for printing a [`Set`](Set). |
303 | pub struct DisplaySet<'a, T> { |
304 | inner: DisplayMap<'a, T>, |
305 | } |
306 | |
307 | impl<'a, T: FmtConst + 'a> fmt::Display for DisplaySet<'a, T> { |
308 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
309 | write!(f, " {}::Set {{ map: {} }}" , self.inner.path, self.inner) |
310 | } |
311 | } |
312 | |
313 | /// A builder for the `phf::OrderedMap` type. |
314 | pub struct OrderedMap<K> { |
315 | keys: Vec<K>, |
316 | values: Vec<String>, |
317 | path: String, |
318 | } |
319 | |
320 | impl<K: Hash + PhfHash + Eq + FmtConst> OrderedMap<K> { |
321 | /// Constructs a enw `phf::OrderedMap` builder. |
322 | pub fn new() -> OrderedMap<K> { |
323 | OrderedMap { |
324 | keys: vec![], |
325 | values: vec![], |
326 | path: String::from("::phf" ), |
327 | } |
328 | } |
329 | |
330 | /// Set the path to the `phf` crate from the global namespace |
331 | pub fn phf_path(&mut self, path: &str) -> &mut OrderedMap<K> { |
332 | self.path = path.to_owned(); |
333 | self |
334 | } |
335 | |
336 | /// Adds an entry to the builder. |
337 | /// |
338 | /// `value` will be written exactly as provided in the constructed source. |
339 | pub fn entry(&mut self, key: K, value: &str) -> &mut OrderedMap<K> { |
340 | self.keys.push(key); |
341 | self.values.push(value.to_owned()); |
342 | self |
343 | } |
344 | |
345 | /// Calculate the hash parameters and return a struct implementing |
346 | /// [`Display`](::std::fmt::Display) which will print the constructed |
347 | /// `phf::OrderedMap`. |
348 | /// |
349 | /// # Panics |
350 | /// |
351 | /// Panics if there are any duplicate keys. |
352 | pub fn build(&self) -> DisplayOrderedMap<'_, K> { |
353 | let mut set = HashSet::new(); |
354 | for key in &self.keys { |
355 | if !set.insert(key) { |
356 | panic!("duplicate key ` {}`" , Delegate(key)); |
357 | } |
358 | } |
359 | |
360 | let state = phf_generator::generate_hash(&self.keys); |
361 | |
362 | DisplayOrderedMap { |
363 | path: &self.path, |
364 | state, |
365 | keys: &self.keys, |
366 | values: &self.values, |
367 | } |
368 | } |
369 | } |
370 | |
371 | /// An adapter for printing a [`OrderedMap`](OrderedMap). |
372 | pub struct DisplayOrderedMap<'a, K> { |
373 | path: &'a str, |
374 | state: HashState, |
375 | keys: &'a [K], |
376 | values: &'a [String], |
377 | } |
378 | |
379 | impl<'a, K: FmtConst + 'a> fmt::Display for DisplayOrderedMap<'a, K> { |
380 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
381 | write!( |
382 | f, |
383 | " {}::OrderedMap {{ |
384 | key: {:?}, |
385 | disps: &[" , |
386 | self.path, self.state.key |
387 | )?; |
388 | for &(d1, d2) in &self.state.disps { |
389 | write!( |
390 | f, |
391 | " |
392 | ( {}, {})," , |
393 | d1, d2 |
394 | )?; |
395 | } |
396 | write!( |
397 | f, |
398 | " |
399 | ], |
400 | idxs: &[" , |
401 | )?; |
402 | for &idx in &self.state.map { |
403 | write!( |
404 | f, |
405 | " |
406 | {}," , |
407 | idx |
408 | )?; |
409 | } |
410 | write!( |
411 | f, |
412 | " |
413 | ], |
414 | entries: &[" , |
415 | )?; |
416 | for (key, value) in self.keys.iter().zip(self.values.iter()) { |
417 | write!( |
418 | f, |
419 | " |
420 | ( {}, {})," , |
421 | Delegate(key), |
422 | value |
423 | )?; |
424 | } |
425 | write!( |
426 | f, |
427 | " |
428 | ], |
429 | }}" |
430 | ) |
431 | } |
432 | } |
433 | |
434 | /// A builder for the `phf::OrderedSet` type. |
435 | pub struct OrderedSet<T> { |
436 | map: OrderedMap<T>, |
437 | } |
438 | |
439 | impl<T: Hash + PhfHash + Eq + FmtConst> OrderedSet<T> { |
440 | /// Constructs a new `phf::OrderedSet` builder. |
441 | pub fn new() -> OrderedSet<T> { |
442 | OrderedSet { |
443 | map: OrderedMap::new(), |
444 | } |
445 | } |
446 | |
447 | /// Set the path to the `phf` crate from the global namespace |
448 | pub fn phf_path(&mut self, path: &str) -> &mut OrderedSet<T> { |
449 | self.map.phf_path(path); |
450 | self |
451 | } |
452 | |
453 | /// Adds an entry to the builder. |
454 | pub fn entry(&mut self, entry: T) -> &mut OrderedSet<T> { |
455 | self.map.entry(entry, "()" ); |
456 | self |
457 | } |
458 | |
459 | /// Calculate the hash parameters and return a struct implementing |
460 | /// [`Display`](::std::fmt::Display) which will print the constructed |
461 | /// `phf::OrderedSet`. |
462 | /// |
463 | /// # Panics |
464 | /// |
465 | /// Panics if there are any duplicate keys. |
466 | pub fn build(&self) -> DisplayOrderedSet<'_, T> { |
467 | DisplayOrderedSet { |
468 | inner: self.map.build(), |
469 | } |
470 | } |
471 | } |
472 | |
473 | /// An adapter for printing a [`OrderedSet`](OrderedSet). |
474 | pub struct DisplayOrderedSet<'a, T> { |
475 | inner: DisplayOrderedMap<'a, T>, |
476 | } |
477 | |
478 | impl<'a, T: FmtConst + 'a> fmt::Display for DisplayOrderedSet<'a, T> { |
479 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
480 | write!( |
481 | f, |
482 | " {}::OrderedSet {{ map: {} }}" , |
483 | self.inner.path, self.inner |
484 | ) |
485 | } |
486 | } |
487 | |