Skip to main content

tidecoin_primitives/script/
builder.rs

1// SPDX-License-Identifier: CC0-1.0
2
3use core::fmt;
4
5use super::{Error, Script, ScriptBuf};
6use crate::opcodes::all::{
7    OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY, OP_CHECKSIG, OP_CHECKSIGVERIFY, OP_EQUAL,
8    OP_EQUALVERIFY, OP_NUMEQUAL, OP_NUMEQUALVERIFY, OP_VERIFY,
9};
10use crate::opcodes::Opcode;
11use crate::prelude::Vec;
12
13/// Script builder.
14#[derive(PartialEq, Eq, Clone)]
15pub struct Builder<T>(ScriptBuf<T>, Option<Opcode>);
16
17impl<T> Builder<T> {
18    /// Creates a new empty builder.
19    pub const fn new() -> Self {
20        Self(ScriptBuf::new(), None)
21    }
22
23    /// Creates a builder with reserved capacity.
24    pub fn with_capacity(capacity: usize) -> Self {
25        Self(ScriptBuf::with_capacity(capacity), None)
26    }
27
28    /// Returns the script length in bytes.
29    pub fn len(&self) -> usize {
30        self.0.len()
31    }
32
33    /// Returns whether the builder is empty.
34    pub fn is_empty(&self) -> bool {
35        self.0.is_empty()
36    }
37
38    /// Pushes an integer.
39    ///
40    /// # Errors
41    ///
42    /// Returns [`Error::NumericOverflow`] when `n` is outside the minimally encodable range.
43    pub fn push_int(mut self, n: i32) -> Result<Self, Error> {
44        self.0.push_int(n)?;
45        self.1 = None;
46        Ok(self)
47    }
48
49    /// Pushes an integer without range checking.
50    #[must_use]
51    pub fn push_int_unchecked(mut self, n: i64) -> Self {
52        self.0.push_int_unchecked(n);
53        self.1 = None;
54        self
55    }
56
57    /// Pushes an integer without numeric-opcode optimization.
58    #[must_use]
59    pub fn push_int_non_minimal(mut self, data: i64) -> Self {
60        self.0.push_int_non_minimal(data);
61        self.1 = None;
62        self
63    }
64
65    /// Pushes a slice.
66    #[must_use]
67    pub fn push_slice<D: AsRef<[u8]>>(mut self, data: D) -> Self {
68        self.0.push_slice(data);
69        self.1 = None;
70        self
71    }
72
73    /// Pushes a slice without minimal-push optimization.
74    #[must_use]
75    pub fn push_slice_non_minimal<D: AsRef<[u8]>>(mut self, data: D) -> Self {
76        self.0.push_slice_non_minimal(data);
77        self.1 = None;
78        self
79    }
80
81    /// Pushes an opcode.
82    #[must_use]
83    pub fn push_opcode(mut self, opcode: Opcode) -> Self {
84        self.0.push_opcode(opcode);
85        self.1 = Some(opcode);
86        self
87    }
88
89    /// Adds `OP_VERIFY` or rewrites the most recent opcode to its VERIFY form when possible.
90    #[must_use]
91    pub fn push_verify(mut self) -> Self {
92        match opcode_to_verify(self.1) {
93            Some(opcode) => {
94                self.0.as_byte_vec().pop();
95                self.push_opcode(opcode)
96            }
97            None => self.push_opcode(OP_VERIFY),
98        }
99    }
100
101    /// Converts into a script.
102    pub fn into_script(self) -> ScriptBuf<T> {
103        self.0
104    }
105
106    /// Converts into raw bytes.
107    pub fn into_bytes(self) -> Vec<u8> {
108        self.0.into_bytes()
109    }
110
111    /// Returns the current script.
112    pub fn as_script(&self) -> &Script<T> {
113        self.0.as_script()
114    }
115
116    /// Returns the raw script bytes.
117    pub fn as_bytes(&self) -> &[u8] {
118        self.0.as_bytes()
119    }
120}
121
122impl<T> Default for Builder<T> {
123    fn default() -> Self {
124        Self::new()
125    }
126}
127
128impl<T> From<Vec<u8>> for Builder<T> {
129    fn from(v: Vec<u8>) -> Self {
130        let script = ScriptBuf::from_bytes(v);
131        let last_op = script.last_opcode();
132        Self(script, last_op)
133    }
134}
135
136impl<T> fmt::Display for Builder<T> {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        fmt::Display::fmt(&self.0, f)
139    }
140}
141
142impl<T> fmt::Debug for Builder<T> {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        fmt::Display::fmt(self, f)
145    }
146}
147
148fn opcode_to_verify(opcode: Option<Opcode>) -> Option<Opcode> {
149    opcode.and_then(|opcode| match opcode {
150        OP_EQUAL => Some(OP_EQUALVERIFY),
151        OP_NUMEQUAL => Some(OP_NUMEQUALVERIFY),
152        OP_CHECKSIG => Some(OP_CHECKSIGVERIFY),
153        OP_CHECKMULTISIG => Some(OP_CHECKMULTISIGVERIFY),
154        _ => None,
155    })
156}