use std::cell::OnceCell;
use std::rc::Rc;
#[derive(Clone)]
pub struct Lazy<T: Clone> {
inner: Rc<LazyInner<T>>,
}
struct LazyInner<T: Clone> {
cache: OnceCell<T>,
compute: std::cell::Cell<Option<Box<dyn FnOnce() -> T>>>,
}
impl<T: Clone> Lazy<T> {
pub fn defer(f: impl FnOnce() -> T + 'static) -> Self {
Self {
inner: Rc::new(LazyInner {
cache: OnceCell::new(),
compute: std::cell::Cell::new(Some(Box::new(f))),
}),
}
}
pub fn ready(value: T) -> Self {
let cache = OnceCell::new();
let _ = cache.set(value);
Self {
inner: Rc::new(LazyInner {
cache,
compute: std::cell::Cell::new(None),
}),
}
}
pub fn demand(&self) -> &T {
self.inner.cache.get_or_init(|| {
let compute = self.inner.compute.take()
.expect("Lazy: cache empty but compute already consumed (bug)");
compute()
})
}
pub fn is_ready(&self) -> bool {
self.inner.cache.get().is_some()
}
}
impl<T: Clone + std::fmt::Debug> std::fmt::Debug for Lazy<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(v) = self.inner.cache.get() {
write!(f, "Lazy({v:?})")
} else {
write!(f, "Lazy(<deferred>)")
}
}
}
#[derive(Clone)]
pub struct FallibleLazy<T: Clone, E: Clone> {
inner: Rc<FallibleLazyInner<T, E>>,
}
struct FallibleLazyInner<T: Clone, E: Clone> {
cache: OnceCell<T>,
compute: std::cell::Cell<Option<Box<dyn FnOnce() -> Result<T, E>>>>,
}
impl<T: Clone, E: Clone> FallibleLazy<T, E> {
pub fn defer(f: impl FnOnce() -> Result<T, E> + 'static) -> Self {
Self {
inner: Rc::new(FallibleLazyInner {
cache: OnceCell::new(),
compute: std::cell::Cell::new(Some(Box::new(f))),
}),
}
}
pub fn ready(value: T) -> Self {
let cache = OnceCell::new();
let _ = cache.set(value);
Self {
inner: Rc::new(FallibleLazyInner {
cache,
compute: std::cell::Cell::new(None),
}),
}
}
pub fn demand(&self) -> Result<&T, E> {
if let Some(v) = self.inner.cache.get() {
return Ok(v);
}
if let Some(compute) = self.inner.compute.take() {
match compute() {
Ok(val) => {
let _ = self.inner.cache.set(val);
Ok(self.inner.cache.get().unwrap())
}
Err(e) => Err(e),
}
} else {
panic!("FallibleLazy: cache empty and compute consumed without storing result")
}
}
pub fn is_ready(&self) -> bool {
self.inner.cache.get().is_some()
}
}
impl<T: Clone + std::fmt::Debug, E: Clone> std::fmt::Debug for FallibleLazy<T, E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(v) = self.inner.cache.get() {
write!(f, "FallibleLazy({v:?})")
} else {
write!(f, "FallibleLazy(<deferred>)")
}
}
}
use crate::value::{Value, EvalError, NixAttrs, intern, resolve};
use sui_intern::Symbol;
pub type LazyValue = FallibleLazy<Value, EvalError>;
#[derive(Clone, Debug)]
pub struct LazyAttrs {
entries: im_rc::HashMap<Symbol, LazyValue, rustc_hash::FxBuildHasher>,
}
impl LazyAttrs {
pub fn new() -> Self {
Self {
entries: im_rc::HashMap::default(),
}
}
pub fn insert(&mut self, key: Symbol, value: LazyValue) {
self.entries.insert(key, value);
}
pub fn insert_ready(&mut self, key: Symbol, value: Value) {
self.entries.insert(key, LazyValue::ready(value));
}
pub fn insert_deferred<F>(&mut self, key: Symbol, f: F)
where
F: FnOnce() -> Result<Value, EvalError> + 'static,
{
self.entries.insert(key, LazyValue::defer(f));
}
pub fn get(&self, key: &str) -> Option<Result<&Value, EvalError>> {
let sym = intern(key);
self.entries.get(&sym).map(|lv| lv.demand())
}
pub fn get_sym(&self, sym: &Symbol) -> Option<Result<&Value, EvalError>> {
self.entries.get(sym).map(|lv| lv.demand())
}
pub fn contains_key(&self, key: &str) -> bool {
self.entries.contains_key(&intern(key))
}
pub fn keys(&self) -> impl Iterator<Item = String> + '_ {
self.entries.keys().map(|s| resolve(*s))
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn update(&self, other: &LazyAttrs) -> LazyAttrs {
let mut result = self.entries.clone();
for (k, v) in other.entries.iter() {
result.insert(*k, v.clone());
}
LazyAttrs { entries: result }
}
pub fn force_all(&self) -> Result<NixAttrs, EvalError> {
let mut attrs = NixAttrs::new();
for (sym, lv) in self.entries.iter() {
let val = lv.demand()?;
attrs.insert(resolve(*sym), val.clone());
}
Ok(attrs)
}
}
impl Default for LazyAttrs {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub enum OverlayAttrs {
Base(Rc<NixAttrs>),
Overlay {
left: Rc<OverlayAttrs>,
right: Rc<NixAttrs>,
},
}
impl OverlayAttrs {
pub fn base(attrs: NixAttrs) -> Self {
OverlayAttrs::Base(Rc::new(attrs))
}
pub fn overlay(self, right: NixAttrs) -> Self {
OverlayAttrs::Overlay {
left: Rc::new(self),
right: Rc::new(right),
}
}
pub fn get(&self, key: &str) -> Option<&Value> {
match self {
OverlayAttrs::Base(attrs) => attrs.get(key),
OverlayAttrs::Overlay { left, right } => {
right.get(key).or_else(|| left.get(key))
}
}
}
pub fn get_sym(&self, sym: &Symbol) -> Option<&Value> {
match self {
OverlayAttrs::Base(attrs) => attrs.get_sym(sym),
OverlayAttrs::Overlay { left, right } => {
right.get_sym(sym).or_else(|| left.get_sym(sym))
}
}
}
pub fn contains_key(&self, key: &str) -> bool {
match self {
OverlayAttrs::Base(attrs) => attrs.contains_key(key),
OverlayAttrs::Overlay { left, right } => {
right.contains_key(key) || left.contains_key(key)
}
}
}
pub fn all_keys(&self) -> Vec<String> {
let mut seen = std::collections::HashSet::new();
let mut result = Vec::new();
self.collect_keys(&mut seen, &mut result);
result.sort();
result
}
fn collect_keys(&self, seen: &mut std::collections::HashSet<String>, result: &mut Vec<String>) {
match self {
OverlayAttrs::Base(attrs) => {
for (k, _) in attrs.iter_unsorted() {
if seen.insert(k.clone()) {
result.push(k);
}
}
}
OverlayAttrs::Overlay { left, right } => {
for (k, _) in right.iter_unsorted() {
if seen.insert(k.clone()) {
result.push(k);
}
}
left.collect_keys(seen, result);
}
}
}
pub fn flatten(&self) -> NixAttrs {
match self {
OverlayAttrs::Base(attrs) => (**attrs).clone(),
OverlayAttrs::Overlay { left, right } => {
let base = left.flatten();
base.update(right)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::Cell;
#[test]
fn deferred_not_evaluated_until_demand() {
let evaluated = Rc::new(Cell::new(false));
let e = evaluated.clone();
let lazy = Lazy::defer(move || {
e.set(true);
42
});
assert!(!evaluated.get());
assert_eq!(*lazy.demand(), 42);
assert!(evaluated.get());
}
#[test]
fn demand_memoizes() {
let count = Rc::new(Cell::new(0));
let c = count.clone();
let lazy = Lazy::defer(move || {
c.set(c.get() + 1);
"hello"
});
assert_eq!(*lazy.demand(), "hello");
assert_eq!(*lazy.demand(), "hello");
assert_eq!(count.get(), 1); }
#[test]
fn ready_is_immediate() {
let lazy = Lazy::ready(99);
assert!(lazy.is_ready());
assert_eq!(*lazy.demand(), 99);
}
#[test]
fn clone_shares_computation() {
let count = Rc::new(Cell::new(0));
let c = count.clone();
let lazy = Lazy::defer(move || {
c.set(c.get() + 1);
7
});
let clone = lazy.clone();
assert_eq!(*lazy.demand(), 7);
assert_eq!(*clone.demand(), 7); assert_eq!(count.get(), 1); }
#[test]
fn fallible_lazy_success() {
let fl: FallibleLazy<i64, String> = FallibleLazy::defer(|| Ok(42));
assert!(!fl.is_ready());
assert_eq!(*fl.demand().unwrap(), 42);
assert!(fl.is_ready());
assert_eq!(*fl.demand().unwrap(), 42); }
#[test]
fn fallible_lazy_ready() {
let fl: FallibleLazy<i64, String> = FallibleLazy::ready(99);
assert!(fl.is_ready());
assert_eq!(*fl.demand().unwrap(), 99);
}
#[test]
fn fallible_lazy_clone_shares() {
let count = Rc::new(Cell::new(0));
let c = count.clone();
let fl: FallibleLazy<i64, String> = FallibleLazy::defer(move || {
c.set(c.get() + 1);
Ok(7)
});
let clone = fl.clone();
assert_eq!(*fl.demand().unwrap(), 7);
assert_eq!(*clone.demand().unwrap(), 7);
assert_eq!(count.get(), 1); }
#[test]
fn lazy_attrs_keys_without_forcing() {
let evaluated = Rc::new(Cell::new(false));
let e = evaluated.clone();
let mut attrs = LazyAttrs::new();
attrs.insert_deferred(intern("expensive"), move || {
e.set(true);
Ok(Value::Int(42))
});
attrs.insert_ready(intern("cheap"), Value::Int(1));
assert_eq!(attrs.len(), 2);
assert!(attrs.contains_key("expensive"));
assert!(attrs.contains_key("cheap"));
assert!(!evaluated.get());
let val = attrs.get("expensive").unwrap().unwrap();
assert_eq!(*val, Value::Int(42));
assert!(evaluated.get()); }
#[test]
fn lazy_attrs_update_no_forcing() {
let evaluated = Rc::new(Cell::new(false));
let e = evaluated.clone();
let mut a = LazyAttrs::new();
a.insert_ready(intern("x"), Value::Int(1));
let mut b = LazyAttrs::new();
b.insert_deferred(intern("y"), move || {
e.set(true);
Ok(Value::Int(2))
});
let merged = a.update(&b);
assert_eq!(merged.len(), 2);
assert!(!evaluated.get());
assert_eq!(*merged.get("x").unwrap().unwrap(), Value::Int(1));
assert!(!evaluated.get()); }
#[test]
fn lazy_attrs_force_all() {
let mut attrs = LazyAttrs::new();
attrs.insert_ready(intern("a"), Value::Int(1));
attrs.insert_deferred(intern("b"), || Ok(Value::Int(2)));
let nix_attrs = attrs.force_all().unwrap();
assert_eq!(nix_attrs.get("a"), Some(&Value::Int(1)));
assert_eq!(nix_attrs.get("b"), Some(&Value::Int(2)));
}
#[test]
fn overlay_get_right_overrides_left() {
let mut left = NixAttrs::new();
left.insert("x".to_string(), Value::Int(1));
left.insert("y".to_string(), Value::Int(2));
let mut right = NixAttrs::new();
right.insert("x".to_string(), Value::Int(10));
let overlay = OverlayAttrs::base(left).overlay(right);
assert_eq!(overlay.get("x"), Some(&Value::Int(10))); assert_eq!(overlay.get("y"), Some(&Value::Int(2))); assert_eq!(overlay.get("z"), None); }
#[test]
fn overlay_chain_three_levels() {
let mut a = NixAttrs::new();
a.insert("x".to_string(), Value::Int(1));
let mut b = NixAttrs::new();
b.insert("y".to_string(), Value::Int(2));
let mut c = NixAttrs::new();
c.insert("x".to_string(), Value::Int(3));
let chain = OverlayAttrs::base(a).overlay(b).overlay(c);
assert_eq!(chain.get("x"), Some(&Value::Int(3))); assert_eq!(chain.get("y"), Some(&Value::Int(2))); }
#[test]
fn overlay_is_o1_construction() {
let mut big = NixAttrs::new();
for i in 0..1000 {
big.insert(format!("attr_{i}"), Value::Int(i));
}
let mut small = NixAttrs::new();
small.insert("target".to_string(), Value::Int(42));
let overlay = OverlayAttrs::base(big).overlay(small);
assert_eq!(overlay.get("target"), Some(&Value::Int(42)));
assert_eq!(overlay.get("attr_0"), Some(&Value::Int(0)));
}
#[test]
fn overlay_all_keys() {
let mut a = NixAttrs::new();
a.insert("x".to_string(), Value::Int(1));
a.insert("y".to_string(), Value::Int(2));
let mut b = NixAttrs::new();
b.insert("y".to_string(), Value::Int(20));
b.insert("z".to_string(), Value::Int(30));
let overlay = OverlayAttrs::base(a).overlay(b);
let keys = overlay.all_keys();
assert_eq!(keys, vec!["x", "y", "z"]); }
#[test]
fn overlay_contains_key() {
let mut a = NixAttrs::new();
a.insert("x".to_string(), Value::Int(1));
let overlay = OverlayAttrs::base(a);
assert!(overlay.contains_key("x"));
assert!(!overlay.contains_key("y"));
}
#[test]
fn overlay_flatten() {
let mut a = NixAttrs::new();
a.insert("x".to_string(), Value::Int(1));
let mut b = NixAttrs::new();
b.insert("x".to_string(), Value::Int(2));
b.insert("y".to_string(), Value::Int(3));
let overlay = OverlayAttrs::base(a).overlay(b);
let flat = overlay.flatten();
assert_eq!(flat.get("x"), Some(&Value::Int(2)));
assert_eq!(flat.get("y"), Some(&Value::Int(3)));
}
}