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