klvm_traits/
lib.rs

1//! # KLVM Traits
2//! This is a library for encoding and decoding Rust values using a KLVM allocator.
3//! It provides implementations for every fixed-width signed and unsigned integer type,
4//! as well as many other values in the standard library that would be common to encode.
5
6#![cfg_attr(feature = "derive", doc = "\n\n")]
7#![cfg_attr(feature = "derive", doc = include_str!("../docs/derive_macros.md"))]
8
9#[cfg(feature = "derive")]
10pub use klvm_derive::*;
11
12mod error;
13mod from_klvm;
14mod int_encoding;
15mod klvm_decoder;
16mod klvm_encoder;
17mod macros;
18mod match_byte;
19mod to_klvm;
20mod wrappers;
21
22pub use error::*;
23pub use from_klvm::*;
24pub use int_encoding::*;
25pub use klvm_decoder::*;
26pub use klvm_encoder::*;
27pub use match_byte::*;
28pub use to_klvm::*;
29pub use wrappers::*;
30
31pub use klvmr::Atom;
32
33#[cfg(test)]
34#[cfg(feature = "derive")]
35mod derive_tests {
36    extern crate self as klvm_traits;
37
38    use super::*;
39
40    use std::fmt::Debug;
41
42    use klvmr::{serde::node_to_bytes, Allocator};
43
44    fn check<T>(value: &T, expected: &str)
45    where
46        T: Debug + PartialEq + ToKlvm<Allocator> + FromKlvm<Allocator>,
47    {
48        let a = &mut Allocator::new();
49
50        let ptr = value.to_klvm(a).unwrap();
51
52        let actual = node_to_bytes(a, ptr).unwrap();
53        assert_eq!(expected, hex::encode(actual));
54
55        let round_trip = T::from_klvm(a, ptr).unwrap();
56        assert_eq!(value, &round_trip);
57    }
58
59    fn coerce_into<A, B>(value: A) -> B
60    where
61        A: ToKlvm<Allocator>,
62        B: FromKlvm<Allocator>,
63    {
64        let a = &mut Allocator::new();
65        let ptr = value.to_klvm(a).unwrap();
66        B::from_klvm(a, ptr).unwrap()
67    }
68
69    #[test]
70    fn test_list_struct() {
71        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
72        #[klvm(list)]
73        struct Struct {
74            a: u64,
75            b: i32,
76        }
77
78        // Includes the nil terminator.
79        check(&Struct { a: 52, b: -32 }, "ff34ff81e080");
80    }
81
82    #[test]
83    fn test_list_struct_with_rest() {
84        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
85        #[klvm(list)]
86        struct Struct {
87            a: u64,
88            #[klvm(rest)]
89            b: i32,
90        }
91
92        // Does not include the nil terminator.
93        check(&Struct { a: 52, b: -32 }, "ff3481e0");
94    }
95
96    #[test]
97    fn test_solution_struct() {
98        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
99        #[klvm(list)]
100        struct Struct {
101            a: u64,
102            b: i32,
103        }
104
105        // Includes the nil terminator.
106        check(&Struct { a: 52, b: -32 }, "ff34ff81e080");
107
108        // Allows additional parameters.
109        let mut allocator = Allocator::new();
110        let ptr = klvm_list!(100, 200, 300, 400)
111            .to_klvm(&mut allocator)
112            .unwrap();
113        let value = Struct::from_klvm(&allocator, ptr).unwrap();
114        assert_eq!(value, Struct { a: 100, b: 200 });
115
116        // Doesn't allow differing types for the actual solution parameters.
117        let mut allocator = Allocator::new();
118        let ptr = klvm_list!([1, 2, 3], 200, 300)
119            .to_klvm(&mut allocator)
120            .unwrap();
121        Struct::from_klvm(&allocator, ptr).unwrap_err();
122    }
123
124    #[test]
125    fn test_solution_struct_with_rest() {
126        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
127        #[klvm(list)]
128        struct Struct {
129            a: u64,
130            #[klvm(rest)]
131            b: i32,
132        }
133
134        // Does not include the nil terminator.
135        check(&Struct { a: 52, b: -32 }, "ff3481e0");
136
137        // Does not allow additional parameters, since it consumes the rest.
138        let mut allocator = Allocator::new();
139        let ptr = klvm_list!(100, 200, 300, 400)
140            .to_klvm(&mut allocator)
141            .unwrap();
142        Struct::from_klvm(&allocator, ptr).unwrap_err();
143    }
144
145    #[test]
146    fn test_curry_struct() {
147        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
148        #[klvm(curry)]
149        struct Struct {
150            a: u64,
151            b: i32,
152        }
153
154        check(
155            &Struct { a: 52, b: -32 },
156            "ff04ffff0134ffff04ffff0181e0ff018080",
157        );
158    }
159
160    #[test]
161    fn test_curry_struct_with_rest() {
162        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
163        #[klvm(curry)]
164        struct Struct {
165            a: u64,
166            #[klvm(rest)]
167            b: i32,
168        }
169
170        check(&Struct { a: 52, b: -32 }, "ff04ffff0134ff81e080");
171    }
172
173    #[test]
174    fn test_tuple_struct() {
175        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
176        #[klvm(list)]
177        struct Struct(String, String);
178
179        check(&Struct("A".to_string(), "B".to_string()), "ff41ff4280");
180    }
181
182    #[test]
183    fn test_newtype_struct() {
184        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
185        #[klvm(list)]
186        struct Struct(#[klvm(rest)] String);
187
188        check(&Struct("XYZ".to_string()), "8358595a");
189    }
190
191    #[test]
192    fn test_optional() {
193        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
194        #[klvm(list)]
195        struct Struct {
196            a: u64,
197            #[klvm(default)]
198            b: Option<i32>,
199        }
200
201        check(
202            &Struct {
203                a: 52,
204                b: Some(-32),
205            },
206            "ff34ff81e080",
207        );
208        check(&Struct { a: 52, b: None }, "ff3480");
209    }
210
211    #[test]
212    fn test_default() {
213        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
214        #[klvm(list)]
215        struct Struct {
216            a: u64,
217            #[klvm(default = 42)]
218            b: i32,
219        }
220
221        check(&Struct { a: 52, b: 32 }, "ff34ff2080");
222        check(&Struct { a: 52, b: 42 }, "ff3480");
223    }
224
225    #[test]
226    fn test_default_owned() {
227        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
228        #[klvm(list)]
229        struct Struct {
230            a: u64,
231            #[klvm(default = "Hello".to_string())]
232            b: String,
233        }
234
235        check(
236            &Struct {
237                a: 52,
238                b: "World".to_string(),
239            },
240            "ff34ff85576f726c6480",
241        );
242        check(
243            &Struct {
244                a: 52,
245                b: "Hello".to_string(),
246            },
247            "ff3480",
248        );
249    }
250
251    #[test]
252    fn test_constants() {
253        #[derive(ToKlvm, FromKlvm)]
254        #[apply_constants]
255        #[derive(Debug, PartialEq)]
256        #[klvm(list)]
257        struct RunTailCondition<P, S> {
258            #[klvm(constant = 51)]
259            opcode: u8,
260            #[klvm(constant = ())]
261            blank_puzzle_hash: (),
262            #[klvm(constant = -113)]
263            magic_amount: i8,
264            puzzle: P,
265            solution: S,
266        }
267
268        check(
269            &RunTailCondition {
270                puzzle: "puzzle".to_string(),
271                solution: "solution".to_string(),
272            },
273            "ff33ff80ff818fff8670757a7a6c65ff88736f6c7574696f6e80",
274        );
275    }
276
277    #[test]
278    fn test_enum() {
279        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
280        #[klvm(list)]
281        enum Enum {
282            A(i32),
283            B { x: i32 },
284            C,
285        }
286
287        check(&Enum::A(32), "ff80ff2080");
288        check(&Enum::B { x: -72 }, "ff01ff81b880");
289        check(&Enum::C, "ff0280");
290    }
291
292    #[test]
293    fn test_explicit_discriminant() {
294        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
295        #[klvm(list)]
296        #[repr(u8)]
297        enum Enum {
298            A(i32) = 42,
299            B { x: i32 } = 34,
300            C = 11,
301        }
302
303        check(&Enum::A(32), "ff2aff2080");
304        check(&Enum::B { x: -72 }, "ff22ff81b880");
305        check(&Enum::C, "ff0b80");
306    }
307
308    #[test]
309    fn test_untagged_enum() {
310        // This has to be a proper list, since it's too ambiguous if extraneous parameters are parsed
311        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
312        #[klvm(proper_list, untagged)]
313        enum Enum {
314            A(i32),
315            B {
316                x: i32,
317                y: i32,
318            },
319            #[klvm(curry)]
320            C {
321                curried_value: String,
322            },
323        }
324
325        check(&Enum::A(32), "ff2080");
326        check(&Enum::B { x: -72, y: 94 }, "ff81b8ff5e80");
327        check(
328            &Enum::C {
329                curried_value: "Hello".to_string(),
330            },
331            "ff04ffff018548656c6c6fff0180",
332        );
333    }
334
335    #[test]
336    fn test_untagged_enum_parsing_order() {
337        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
338        #[klvm(list, untagged)]
339        enum Enum {
340            // This variant is parsed first, so `B` will never be deserialized.
341            A(i32),
342            // When `B` is serialized, it will round trip as `A` instead.
343            B(i32),
344            // `C` will be deserialized as a fallback when the bytes don't deserialize to a valid `i32`.
345            C(String),
346        }
347
348        // This round trips to the same value, since `A` is parsed first.
349        assert_eq!(coerce_into::<Enum, Enum>(Enum::A(32)), Enum::A(32));
350
351        // This round trips to `A` instead of `B`, since `A` is parsed first.
352        assert_eq!(coerce_into::<Enum, Enum>(Enum::B(32)), Enum::A(32));
353
354        // This round trips to `A` instead of `C`, since the bytes used to represent
355        // this string are also a valid `i32` value.
356        assert_eq!(
357            coerce_into::<Enum, Enum>(Enum::C("Hi".into())),
358            Enum::A(18537)
359        );
360
361        // This round trips to `C` instead of `A`, since the bytes used to represent
362        // this string exceed the size of `i32`.
363        assert_eq!(
364            coerce_into::<Enum, Enum>(Enum::C("Hello, world!".into())),
365            Enum::C("Hello, world!".into())
366        );
367    }
368
369    #[test]
370    fn test_custom_crate_name() {
371        use klvm_traits as klvm_traits2;
372        #[derive(Debug, ToKlvm, FromKlvm, PartialEq)]
373        #[klvm(list, crate_name = klvm_traits2)]
374        struct Struct {
375            a: u64,
376            b: i32,
377        }
378
379        check(&Struct { a: 52, b: -32 }, "ff34ff81e080");
380    }
381}