use std::cell::RefCell;
use std::fmt;
use allocative::Allocative;
use nerpa_core::attribute::{Choice, Fact};
use nerpa_core::secret::Source;
use nerpa_core::{Address, Reference, Target};
use starlark::any::ProvidesStaticType;
use starlark::starlark_simple_value;
use starlark::values::{Heap, NoSerialize, StarlarkValue, Value, starlark_value};
thread_local! {
static BRANCHED_ON: RefCell<Vec<String>> = const { RefCell::new(Vec::new()) };
}
pub(crate) fn forget_branches() {
BRANCHED_ON.with_borrow_mut(Vec::clear);
}
pub(crate) fn branches_taken() -> Vec<String> {
BRANCHED_ON.with_borrow(Clone::clone)
}
#[derive(Debug, thiserror::Error)]
#[error("{subject} cannot be used with {operation}; pass it to another resource instead")]
struct Refused {
subject: String,
operation: &'static str,
}
fn refuse<T>(subject: String, operation: &'static str) -> starlark::Result<T> {
Err(starlark::Error::new_value(Refused { subject, operation }))
}
#[derive(Debug, Clone, ProvidesStaticType, NoSerialize, Allocative)]
pub(crate) struct ResourceRef {
#[allocative(skip)]
address: Address,
}
impl ResourceRef {
pub(crate) fn new(address: Address) -> Self {
Self { address }
}
pub(crate) fn address(&self) -> &Address {
&self.address
}
}
impl fmt::Display for ResourceRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.address)
}
}
starlark_simple_value!(ResourceRef);
#[starlark_value(type = "resource")]
impl<'v> StarlarkValue<'v> for ResourceRef {
fn get_attr(&self, attribute: &str, heap: Heap<'v>) -> Option<Value<'v>> {
Some(heap.alloc(Handle::new(Reference::to(self.address.clone(), attribute))))
}
fn has_attr(&self, _attribute: &str, _heap: Heap<'v>) -> bool {
true
}
}
#[derive(Debug, Clone, ProvidesStaticType, NoSerialize, Allocative)]
pub(crate) struct Handle {
#[allocative(skip)]
reference: Reference,
}
impl Handle {
pub(crate) fn new(reference: Reference) -> Self {
Self { reference }
}
pub(crate) fn reference(&self) -> &Reference {
&self.reference
}
}
impl fmt::Display for Handle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<{}, unknown until apply>", self.reference)
}
}
starlark_simple_value!(Handle);
#[starlark_value(type = "unresolved")]
impl<'v> StarlarkValue<'v> for Handle {
fn equals(&self, _other: Value<'v>) -> starlark::Result<bool> {
refuse(self.to_string(), "==")
}
fn compare(&self, _other: Value<'v>) -> starlark::Result<std::cmp::Ordering> {
refuse(self.to_string(), "an ordering comparison")
}
fn at(&self, _index: Value<'v>, _heap: Heap<'v>) -> starlark::Result<Value<'v>> {
refuse(self.to_string(), "indexing")
}
fn iterate_collect(&self, _heap: Heap<'v>) -> starlark::Result<Vec<Value<'v>>> {
refuse(self.to_string(), "iteration")
}
#[dacc_derive::doc_anchor(id = "inv-plan-001-1")]
fn to_bool(&self) -> bool {
BRANCHED_ON.with_borrow_mut(|taken| taken.push(self.reference.to_string()));
true
}
}
#[derive(Debug, Clone, ProvidesStaticType, NoSerialize, Allocative)]
pub(crate) struct SecretRef {
#[allocative(skip)]
source: Source,
}
impl SecretRef {
pub(crate) fn new(source: Source) -> Self {
Self { source }
}
pub(crate) fn source(&self) -> &Source {
&self.source
}
}
impl fmt::Display for SecretRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<secret from {}>", self.source)
}
}
starlark_simple_value!(SecretRef);
#[starlark_value(type = "secret")]
impl<'v> StarlarkValue<'v> for SecretRef {
fn equals(&self, _other: Value<'v>) -> starlark::Result<bool> {
refuse(self.to_string(), "==")
}
fn compare(&self, _other: Value<'v>) -> starlark::Result<std::cmp::Ordering> {
refuse(self.to_string(), "an ordering comparison")
}
fn at(&self, _index: Value<'v>, _heap: Heap<'v>) -> starlark::Result<Value<'v>> {
refuse(self.to_string(), "indexing")
}
fn iterate_collect(&self, _heap: Heap<'v>) -> starlark::Result<Vec<Value<'v>>> {
refuse(self.to_string(), "iteration")
}
#[dacc_derive::doc_anchor(id = "inv-secret-004-1")]
fn to_bool(&self) -> bool {
BRANCHED_ON.with_borrow_mut(|taken| taken.push(self.to_string()));
true
}
}
#[derive(Debug, Clone, ProvidesStaticType, NoSerialize, Allocative)]
pub(crate) struct NodeRef {
#[allocative(skip)]
node: Target,
#[allocative(skip)]
values: std::collections::BTreeMap<String, nerpa_core::Value>,
}
impl NodeRef {
pub(crate) fn new(
node: Target,
values: std::collections::BTreeMap<String, nerpa_core::Value>,
) -> Self {
Self { node, values }
}
}
impl fmt::Display for NodeRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.node)
}
}
starlark_simple_value!(NodeRef);
#[starlark_value(type = "node")]
impl<'v> StarlarkValue<'v> for NodeRef {
fn get_attr(&self, attribute: &str, heap: Heap<'v>) -> Option<Value<'v>> {
Some(heap.alloc(FactRef::new(Fact::of(self.node.clone(), attribute))))
}
fn has_attr(&self, _attribute: &str, _heap: Heap<'v>) -> bool {
true
}
fn at(&self, index: Value<'v>, heap: Heap<'v>) -> starlark::Result<Value<'v>> {
let Some(key) = index.unpack_str() else {
return refuse(self.to_string(), "an index that is not text");
};
match self.values.get(key) {
Some(nerpa_core::Value::Text(text)) => Ok(heap.alloc(text.as_str())),
Some(nerpa_core::Value::Integer(number)) => Ok(heap.alloc(*number)),
Some(nerpa_core::Value::Boolean(flag)) => Ok(Value::new_bool(*flag)),
Some(other) => Ok(heap.alloc(format!("{other}"))),
None => Err(starlark::Error::new_value(Missing {
node: self.node.to_string(),
key: key.to_owned(),
})),
}
}
}
#[derive(Debug, thiserror::Error)]
#[error(
"the fleet says nothing about {node}'s {key:?}; a value that is not written \
down is not a value with a default"
)]
struct Missing {
node: String,
key: String,
}
#[derive(Debug, Clone, ProvidesStaticType, NoSerialize, Allocative)]
pub(crate) struct FactRef {
#[allocative(skip)]
fact: Fact,
}
impl FactRef {
pub(crate) fn new(fact: Fact) -> Self {
Self { fact }
}
pub(crate) fn fact(&self) -> &Fact {
&self.fact
}
}
impl fmt::Display for FactRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<{}, read from the machine>", self.fact)
}
}
starlark_simple_value!(FactRef);
#[starlark_value(type = "fact")]
impl<'v> StarlarkValue<'v> for FactRef {
fn equals(&self, _other: Value<'v>) -> starlark::Result<bool> {
refuse(self.to_string(), "==")
}
fn compare(&self, _other: Value<'v>) -> starlark::Result<std::cmp::Ordering> {
refuse(self.to_string(), "an ordering comparison")
}
fn at(&self, _index: Value<'v>, _heap: Heap<'v>) -> starlark::Result<Value<'v>> {
refuse(self.to_string(), "indexing")
}
fn iterate_collect(&self, _heap: Heap<'v>) -> starlark::Result<Vec<Value<'v>>> {
refuse(self.to_string(), "iteration")
}
#[dacc_derive::doc_anchor(id = "inv-fact-001-1")]
fn to_bool(&self) -> bool {
BRANCHED_ON.with_borrow_mut(|taken| taken.push(self.to_string()));
true
}
}
#[derive(Debug, Clone, ProvidesStaticType, NoSerialize, Allocative)]
pub(crate) struct ChoiceRef {
#[allocative(skip)]
choice: Choice,
}
impl ChoiceRef {
pub(crate) fn new(choice: Choice) -> Self {
Self { choice }
}
pub(crate) fn choice(&self) -> &Choice {
&self.choice
}
}
impl fmt::Display for ChoiceRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<{}>", self.choice)
}
}
starlark_simple_value!(ChoiceRef);
#[starlark_value(type = "choice")]
impl<'v> StarlarkValue<'v> for ChoiceRef {
fn equals(&self, _other: Value<'v>) -> starlark::Result<bool> {
refuse(self.to_string(), "==")
}
fn compare(&self, _other: Value<'v>) -> starlark::Result<std::cmp::Ordering> {
refuse(self.to_string(), "an ordering comparison")
}
fn at(&self, _index: Value<'v>, _heap: Heap<'v>) -> starlark::Result<Value<'v>> {
refuse(self.to_string(), "indexing")
}
fn iterate_collect(&self, _heap: Heap<'v>) -> starlark::Result<Vec<Value<'v>>> {
refuse(self.to_string(), "iteration")
}
#[dacc_derive::doc_anchor(id = "inv-fact-001-2")]
fn to_bool(&self) -> bool {
BRANCHED_ON.with_borrow_mut(|taken| taken.push(self.to_string()));
true
}
}