1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// SPDX-License-Identifier: CC0-1.0

//! Disconnect Nodes
//!
//! This module defines the [`Disconnectable`] trait, which is applied to the
//! right child of `disconnect` Simplicity nodes, allowing them to represent
//! "disconnected expressions" which may or may not be present, and which
//! contribute to a node's IMR but not CMR.

use crate::dag::Dag;

use std::sync::Arc;

/// Null data type used as dummy for [`Marker::Disconnect`]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct NoDisconnect;

/// Trait representing a "disconnected expression".
pub trait Disconnectable<L> {
    /// Given a generic left child, and treating `self` as the disconnected right
    /// child, produce a [`Dag`] node representing a disconnect node.
    ///
    /// If the disconnected expression is present, this should yield a [`Dag::Binary`].
    /// If it is not present, this should yield a [`Dag::Unary`].
    fn disconnect_dag_arc(self, other: Arc<L>) -> Dag<Arc<L>>;

    /// Same as [`Disconnectable::disconnect_dag_ref`] but takes self by reference.
    fn disconnect_dag_ref<'s>(&'s self, other: &'s L) -> Dag<&'s L>;
}

// Rust's coherence rules prevent us from just doing a small set of blanket
// implementations; one for NoDisconnect (always yielding Dag::Unary) and
// one for T: Borrow<L> (always yielding Dag::Binary) and optional variants.
//
// Instead we have this messy and incomplete list.

// `NoDisconnect` works with arbitrary Arcs
impl<L> Disconnectable<L> for NoDisconnect {
    fn disconnect_dag_arc(self, other: Arc<L>) -> Dag<Arc<L>> {
        Dag::Unary(other)
    }

    fn disconnect_dag_ref<'s>(&'s self, other: &'s L) -> Dag<&'s L> {
        Dag::Unary(other)
    }
}

// Arbitrary things (references, Arcs, whatever) work with themselves.
// This blanket impl is why we can't have a blanket impl for NoDisconnect.
impl<L> Disconnectable<L> for Arc<L> {
    fn disconnect_dag_arc(self, other: Arc<L>) -> Dag<Arc<L>> {
        Dag::Binary(other, self)
    }

    fn disconnect_dag_ref<'s>(&'s self, other: &'s L) -> Dag<&'s L> {
        Dag::Binary(other, self)
    }
}

// Then Option<Arc> can work with either Arcs or references.
impl<L> Disconnectable<L> for Option<Arc<L>> {
    fn disconnect_dag_arc(self, other: Arc<L>) -> Dag<Arc<L>> {
        match self {
            Some(right) => Dag::Binary(other, right),
            None => Dag::Unary(other),
        }
    }

    fn disconnect_dag_ref<'s>(&'s self, other: &'s L) -> Dag<&'s L> {
        match self {
            Some(right) => Dag::Binary(other, right),
            None => Dag::Unary(other),
        }
    }
}