summaryrefslogtreecommitdiff
path: root/dhall/src/semantics/nze/lazy.rs
diff options
context:
space:
mode:
Diffstat (limited to 'dhall/src/semantics/nze/lazy.rs')
-rw-r--r--dhall/src/semantics/nze/lazy.rs64
1 files changed, 64 insertions, 0 deletions
diff --git a/dhall/src/semantics/nze/lazy.rs b/dhall/src/semantics/nze/lazy.rs
new file mode 100644
index 0000000..d361313
--- /dev/null
+++ b/dhall/src/semantics/nze/lazy.rs
@@ -0,0 +1,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()
+ }
+ }
+}