simplicity/node/
disconnect.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Disconnect Nodes
4//!
5//! This module defines the [`Disconnectable`] trait, which is applied to the
6//! right child of `disconnect` Simplicity nodes, allowing them to represent
7//! "disconnected expressions" which may or may not be present, and which
8//! contribute to a node's IHR but not CMR.
9
10use crate::dag::Dag;
11
12use std::sync::Arc;
13
14/// Null data type used as dummy for [`crate::node::Marker::Disconnect`]
15#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
16pub struct NoDisconnect;
17
18/// Trait representing a "disconnected expression".
19pub trait Disconnectable<L> {
20    /// Given a generic left child, and treating `self` as the disconnected right
21    /// child, produce a [`Dag`] node representing a disconnect node.
22    ///
23    /// If the disconnected expression is present, this should yield a [`Dag::Binary`].
24    /// If it is not present, this should yield a [`Dag::Unary`].
25    fn disconnect_dag_arc(self, other: Arc<L>) -> Dag<Arc<L>>;
26
27    /// Same as [`Disconnectable::disconnect_dag_ref`] but takes self by reference.
28    fn disconnect_dag_ref<'s>(&'s self, other: &'s L) -> Dag<&'s L>;
29}
30
31// Rust's coherence rules prevent us from just doing a small set of blanket
32// implementations; one for NoDisconnect (always yielding Dag::Unary) and
33// one for T: Borrow<L> (always yielding Dag::Binary) and optional variants.
34//
35// Instead we have this messy and incomplete list.
36
37// `NoDisconnect` works with arbitrary Arcs
38impl<L> Disconnectable<L> for NoDisconnect {
39    fn disconnect_dag_arc(self, other: Arc<L>) -> Dag<Arc<L>> {
40        Dag::Unary(other)
41    }
42
43    fn disconnect_dag_ref<'s>(&'s self, other: &'s L) -> Dag<&'s L> {
44        Dag::Unary(other)
45    }
46}
47
48// Arbitrary things (references, Arcs, whatever) work with themselves.
49// This blanket impl is why we can't have a blanket impl for NoDisconnect.
50impl<L> Disconnectable<L> for Arc<L> {
51    fn disconnect_dag_arc(self, other: Arc<L>) -> Dag<Arc<L>> {
52        Dag::Binary(other, self)
53    }
54
55    fn disconnect_dag_ref<'s>(&'s self, other: &'s L) -> Dag<&'s L> {
56        Dag::Binary(other, self)
57    }
58}
59
60// Then Option<Arc> can work with either Arcs or references.
61impl<L> Disconnectable<L> for Option<Arc<L>> {
62    fn disconnect_dag_arc(self, other: Arc<L>) -> Dag<Arc<L>> {
63        match self {
64            Some(right) => Dag::Binary(other, right),
65            None => Dag::Unary(other),
66        }
67    }
68
69    fn disconnect_dag_ref<'s>(&'s self, other: &'s L) -> Dag<&'s L> {
70        match self {
71            Some(right) => Dag::Binary(other, right),
72            None => Dag::Unary(other),
73        }
74    }
75}
76
77// `Arc<str>` is like `NoDisconnect` but it names the expression that should fill the hole
78impl<L> Disconnectable<L> for Arc<str> {
79    fn disconnect_dag_arc(self, other: Arc<L>) -> Dag<Arc<L>> {
80        Dag::Unary(other)
81    }
82
83    fn disconnect_dag_ref<'s>(&'s self, other: &'s L) -> Dag<&'s L> {
84        Dag::Unary(other)
85    }
86}