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}