#![doc(html_root_url = "https://docs.rs/build_uuid/0.3.0")]
#![warn(
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
trivial_casts,
trivial_numeric_casts,
unused_import_braces,
unused_qualifications,
unused_results,
clippy::pedantic
)] #![allow(clippy::must_use_candidate)]
use once_cell::sync::Lazy;
use std::{
any::TypeId,
env,
fs::File,
hash::{Hash, Hasher},
io,
};
use uuid::Uuid;
static BUILD_ID: Lazy<Uuid> = Lazy::new(calculate);
#[inline]
pub fn get() -> Uuid {
*BUILD_ID
}
#[allow(clippy::needless_pass_by_value)]
fn from_header<H: Hasher>(_hasher: H) -> Result<H, ()> {
Err(())
}
fn from_exe<H: Hasher>(mut hasher: H) -> Result<H, ()> {
#[cfg(not(target_arch = "wasm32"))]
{
if cfg!(miri) {
return Err(());
}
let file = File::open(env::current_exe().map_err(drop)?).map_err(drop)?;
let _ = io::copy(&mut &file, &mut HashWriter(&mut hasher)).map_err(drop)?;
Ok(hasher)
}
#[cfg(target_arch = "wasm32")]
{
let _ = &mut hasher;
Err(())
}
}
fn from_type_id<H: Hasher>(mut hasher: H) -> Result<H, ()> {
fn type_id_of<T: 'static>(_: &T) -> TypeId {
TypeId::of::<T>()
}
TypeId::of::<()>().hash(&mut hasher);
TypeId::of::<u8>().hash(&mut hasher);
let a = |x: ()| x;
type_id_of(&a).hash(&mut hasher);
let b = |x: u8| x;
type_id_of(&b).hash(&mut hasher);
Ok(hasher)
}
fn calculate() -> Uuid {
let hasher = twox_hash::XxHash::with_seed(0);
let hasher = from_header(hasher)
.or_else(|()| from_exe(hasher))
.unwrap_or(hasher);
let mut hasher = from_type_id(hasher).unwrap();
let mut bytes = [0; 16];
<byteorder::NativeEndian as byteorder::ByteOrder>::write_u64(&mut bytes[..8], hasher.finish());
hasher.write_u8(0);
<byteorder::NativeEndian as byteorder::ByteOrder>::write_u64(&mut bytes[8..], hasher.finish());
uuid::Builder::from_bytes(bytes)
.with_variant(uuid::Variant::RFC4122)
.with_version(uuid::Version::Random)
.into_uuid()
}
struct HashWriter<T: Hasher>(T);
impl<T: Hasher> io::Write for HashWriter<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf);
Ok(buf.len())
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.write(buf).map(|_| ())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[cfg(test)]
mod test {
use wasm_bindgen_test::wasm_bindgen_test;
#[test]
#[wasm_bindgen_test]
fn brute() {
let x = super::calculate();
for _ in 0..1000 {
assert_eq!(x, super::calculate());
}
}
#[test]
#[wasm_bindgen_test]
fn get() {
let x = super::calculate();
assert_eq!(x, super::get());
assert_eq!(x, super::get());
assert_eq!(x, super::get());
}
}