summaryrefslogtreecommitdiff
path: root/dhall/src/semantics/nze/lazy.rs
blob: d361313ca195c735d063891ae914945aa8e982fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
use once_cell::unsync::OnceCell;
use std::cell::Cell;
use std::fmt::Debug;
use std::ops::Deref;

pub trait Eval<Tgt> {
    fn eval(self) -> Tgt;
}

/// A value which is initialized from a `Src` on the first access.
pub struct Lazy<Src, Tgt> {
    /// Exactly one of `src` of `tgt` must be set at a given time.
    /// Once `src` is unset and `tgt` is set, we never go back.
    src: Cell<Option<Src>>,
    tgt: OnceCell<Tgt>,
}

impl<Src, Tgt> Lazy<Src, Tgt>
where
    Src: Eval<Tgt>,
{
    /// Creates a new lazy value with the given initializing value.
    pub fn new(src: Src) -> Self {
        Lazy {
            src: Cell::new(Some(src)),
            tgt: OnceCell::new(),
        }
    }
    /// Creates a new lazy value with the given already-initialized value.
    pub fn new_completed(tgt: Tgt) -> Self {
        let lazy = Lazy {
            src: Cell::new(None),
            tgt: OnceCell::new(),
        };
        let _ = lazy.tgt.set(tgt);
        lazy
    }
}

impl<Src, Tgt> Deref for Lazy<Src, Tgt>
where
    Src: Eval<Tgt>,
{
    type Target = Tgt;
    fn deref(&self) -> &Self::Target {
        self.tgt.get_or_init(|| {
            let src = self.src.take().unwrap();
            src.eval()
        })
    }
}

impl<Src, Tgt> Debug for Lazy<Src, Tgt>
where
    Tgt: Debug,
{
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(tgt) = self.tgt.get() {
            fmt.debug_tuple("Lazy@Init").field(tgt).finish()
        } else {
            fmt.debug_tuple("Lazy@Uninit").finish()
        }
    }
}