uselesskey_core/factory.rs
1use alloc::sync::Arc;
2use core::any::Any;
3use core::fmt;
4
5use crate::id::{ArtifactDomain, Seed};
6use uselesskey_core_factory::Factory as CoreFactory;
7
8/// How a [`Factory`] generates artifacts.
9///
10/// # Examples
11///
12/// ```
13/// use uselesskey_core::{Factory, Mode, Seed};
14///
15/// // Check if a factory is in random or deterministic mode
16/// let fx = Factory::random();
17/// assert!(matches!(fx.mode(), Mode::Random));
18///
19/// let seed = Seed::from_env_value("test").unwrap();
20/// let fx = Factory::deterministic(seed);
21/// assert!(matches!(fx.mode(), Mode::Deterministic { .. }));
22/// ```
23#[derive(Clone)]
24pub struct Factory {
25 inner: CoreFactory,
26}
27
28pub use uselesskey_core_factory::Mode;
29
30impl fmt::Debug for Factory {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 self.inner.fmt(f)
33 }
34}
35
36impl Factory {
37 /// Create a new factory with the specified mode.
38 pub fn new(mode: Mode) -> Self {
39 Self {
40 inner: CoreFactory::new(mode),
41 }
42 }
43
44 /// Create a factory in random mode.
45 ///
46 /// Each process run produces different artifacts, but within a process,
47 /// artifacts are cached by `(domain, label, spec, variant)`.
48 pub fn random() -> Self {
49 Self::new(Mode::Random)
50 }
51
52 /// Create a deterministic factory from an existing seed.
53 pub fn deterministic(master: Seed) -> Self {
54 Self::new(Mode::Deterministic { master })
55 }
56
57 /// Create a deterministic factory from an environment variable.
58 ///
59 /// The environment variable can contain:
60 /// - A 64-character hex string (with optional `0x` prefix)
61 /// - Any other string (hashed to produce a 32-byte seed)
62 ///
63 /// # Errors
64 ///
65 /// Returns an error if the environment variable is not set.
66 #[cfg(feature = "std")]
67 pub fn deterministic_from_env(var: &str) -> Result<Self, crate::Error> {
68 let raw = std::env::var(var).map_err(|_| crate::Error::MissingEnvVar {
69 var: var.to_string(),
70 })?;
71
72 let seed = Seed::from_env_value(&raw).map_err(|message| crate::Error::InvalidSeed {
73 var: var.to_string(),
74 message,
75 })?;
76
77 Ok(Self::deterministic(seed))
78 }
79
80 /// Returns the mode this factory is operating in.
81 pub fn mode(&self) -> &Mode {
82 self.inner.mode()
83 }
84
85 /// Clear the artifact cache.
86 pub fn clear_cache(&self) {
87 self.inner.clear_cache()
88 }
89
90 /// Get a cached artifact by `(domain, label, spec, variant)` or generate one.
91 pub fn get_or_init<T, F>(
92 &self,
93 domain: ArtifactDomain,
94 label: &str,
95 spec_bytes: &[u8],
96 variant: &str,
97 init: F,
98 ) -> Arc<T>
99 where
100 T: Any + Send + Sync + 'static,
101 F: FnOnce(&mut rand_chacha::ChaCha20Rng) -> T,
102 {
103 self.inner
104 .get_or_init(domain, label, spec_bytes, variant, init)
105 }
106}