Skip to main content

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}