miniscript_core_sys/
lib.rs1#![allow(non_upper_case_globals)]
22#![allow(non_camel_case_types)]
23#![allow(non_snake_case)]
24
25include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
27
28use std::ffi::{CStr, CString};
29use std::fmt;
30use std::ptr;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum Context {
35 Wsh,
37 Tapscript,
39}
40
41impl From<Context> for MiniscriptContext {
42 fn from(ctx: Context) -> Self {
43 match ctx {
44 Context::Wsh => MiniscriptContext::MINISCRIPT_CONTEXT_WSH,
45 Context::Tapscript => MiniscriptContext::MINISCRIPT_CONTEXT_TAPSCRIPT,
46 }
47 }
48}
49
50#[derive(Debug)]
52pub struct Error {
53 message: String,
54}
55
56impl fmt::Display for Error {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 write!(f, "{}", self.message)
59 }
60}
61
62impl std::error::Error for Error {}
63
64pub struct Miniscript {
68 ptr: *mut MiniscriptNode,
69 context: Context,
70}
71
72unsafe impl Send for Miniscript {}
75
76unsafe impl Sync for Miniscript {}
78
79impl Miniscript {
80 pub fn from_str(input: &str, context: Context) -> Result<Self, Error> {
91 let c_input = CString::new(input).map_err(|_| Error {
92 message: "input contains null byte".to_string(),
93 })?;
94
95 let mut node_ptr: *mut MiniscriptNode = ptr::null_mut();
96
97 let result =
99 unsafe { miniscript_from_string(c_input.as_ptr(), context.into(), &mut node_ptr) };
100
101 if result.success {
102 Ok(Miniscript {
103 ptr: node_ptr,
104 context,
105 })
106 } else {
107 let message = if !result.error_message.is_null() {
108 let msg = unsafe { CStr::from_ptr(result.error_message) }
110 .to_string_lossy()
111 .into_owned();
112 unsafe { miniscript_free_string(result.error_message) };
113 msg
114 } else {
115 "unknown error".to_string()
116 };
117 Err(Error { message })
118 }
119 }
120
121 pub fn to_string(&self) -> Option<String> {
123 let c_str = unsafe { miniscript_to_string(self.ptr) };
125 if c_str.is_null() {
126 return None;
127 }
128
129 let result = unsafe { CStr::from_ptr(c_str) }
131 .to_string_lossy()
132 .into_owned();
133 unsafe { miniscript_free_string(c_str) };
134
135 Some(result)
136 }
137
138 pub fn is_valid(&self) -> bool {
140 unsafe { miniscript_is_valid(self.ptr) }
142 }
143
144 pub fn is_sane(&self) -> bool {
151 unsafe { miniscript_is_sane(self.ptr) }
153 }
154
155 pub fn get_type(&self) -> Option<String> {
159 let c_str = unsafe { miniscript_get_type(self.ptr) };
161 if c_str.is_null() {
162 return None;
163 }
164
165 let result = unsafe { CStr::from_ptr(c_str) }
167 .to_string_lossy()
168 .into_owned();
169 unsafe { miniscript_free_string(c_str) };
170
171 Some(result)
172 }
173
174 pub fn max_satisfaction_size(&self) -> Option<usize> {
176 let mut size: usize = 0;
177 if unsafe { miniscript_max_satisfaction_size(self.ptr, &mut size) } {
179 Some(size)
180 } else {
181 None
182 }
183 }
184
185 pub fn context(&self) -> Context {
187 self.context
188 }
189}
190
191impl Drop for Miniscript {
192 fn drop(&mut self) {
193 if !self.ptr.is_null() {
194 unsafe { miniscript_node_free(self.ptr) };
196 }
197 }
198}
199
200impl fmt::Debug for Miniscript {
201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202 f.debug_struct("Miniscript")
203 .field("context", &self.context)
204 .field("string", &self.to_string())
205 .field("type", &self.get_type())
206 .finish()
207 }
208}
209
210pub fn version() -> &'static str {
212 unsafe {
214 CStr::from_ptr(miniscript_version())
215 .to_str()
216 .unwrap_or("unknown")
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223
224 #[test]
225 fn test_version() {
226 let v = version();
227 assert!(!v.is_empty());
228 println!("Version: {}", v);
229 }
230
231 #[test]
234 #[ignore]
235 fn test_parse_simple() {
236 let ms = Miniscript::from_str("pk(A)", Context::Wsh).expect("should parse");
237 assert!(ms.is_valid());
238 assert_eq!(ms.to_string(), Some("pk(A)".to_string()));
239 }
240
241 #[test]
242 #[ignore]
243 fn test_parse_and_v() {
244 let ms = Miniscript::from_str("and_v(v:pk(A),pk(B))", Context::Wsh).expect("should parse");
245 assert!(ms.is_valid());
246 }
247
248 #[test]
249 #[ignore]
250 fn test_invalid_miniscript() {
251 let result = Miniscript::from_str("invalid", Context::Wsh);
252 assert!(result.is_err());
253 }
254
255 #[test]
256 #[ignore]
257 fn test_type_properties() {
258 let ms = Miniscript::from_str("pk(A)", Context::Wsh).expect("should parse");
259 let type_str = ms.get_type().expect("should have type");
260 assert!(type_str.contains('B'));
262 }
263}