soroban_env_common/
convert.rs

1use crate::xdr::int128_helpers;
2use crate::{
3    num::{i256_from_pieces, i256_into_pieces, u256_from_pieces, u256_into_pieces},
4    DurationSmall, DurationVal, Env, I128Small, I128Val, I256Small, I256Val, I64Small,
5    TimepointSmall, TimepointVal, U128Small, U128Val, U256Small, U256Val, U64Small, U64Val, Val,
6    I256, U256,
7};
8use core::fmt::Debug;
9
10#[cfg(feature = "std")]
11use crate::xdr::{
12    Duration, Int128Parts, Int256Parts, ScVal, TimePoint, UInt128Parts, UInt256Parts,
13};
14#[cfg(feature = "std")]
15use crate::{
16    num, object::ScValObjRef, val::ValConvert, ConversionError, Error, Object, ScValObject,
17    SymbolSmall, Tag,
18};
19
20/// General trait representing a the ability of some object to perform a
21/// (possibly unsuccessful) conversion between two other types.
22pub trait Convert<F, T> {
23    type Error: Debug + Into<crate::Error>;
24    fn convert(&self, f: F) -> Result<T, Self::Error>;
25}
26
27/// The opposite trait to [`TryFromVal`], analogous to the way that [`TryInto`]
28/// exists as an opposite to [`TryFrom`]. Exists only for convenience of doing
29/// conversions via `.try_into_val(e)` or specifying convertability with a bound
30/// like `TryIntoVal<E,Other>`.
31pub trait TryIntoVal<E: Env, V> {
32    type Error: Debug + Into<crate::Error>;
33    fn try_into_val(&self, env: &E) -> Result<V, Self::Error>;
34}
35
36/// Trait for types that can be fallibly converted to another type `V`, analogous
37/// to the standard Rust type [`TryFrom`], but making use of the provided `Env`
38/// implementation `E` in order to convert parts of the type that require it.
39/// Mainly this exists because `Val` types that contain object handles need to
40/// delegate to the environment to look up and extract the content of those
41/// handles.
42pub trait TryFromVal<E: Env, V: ?Sized>: Sized {
43    type Error: Debug + Into<crate::Error>;
44    fn try_from_val(env: &E, v: &V) -> Result<Self, Self::Error>;
45}
46
47// Blanket-impl uses of TryIntoVal to TryFromVal, so that we
48// only ever have to impl TryFromVal.
49impl<E: Env, T, U> TryIntoVal<E, T> for U
50where
51    T: TryFromVal<E, U>,
52{
53    type Error = T::Error;
54
55    fn try_into_val(&self, env: &E) -> Result<T, Self::Error> {
56        T::try_from_val(env, self)
57    }
58}
59
60// i64 conversions
61
62impl<E: Env> TryFromVal<E, Val> for i64 {
63    type Error = crate::Error;
64
65    fn try_from_val(env: &E, val: &Val) -> Result<Self, Self::Error> {
66        let val = *val;
67        if let Ok(so) = I64Small::try_from(val) {
68            Ok(so.into())
69        } else {
70            let obj = val.try_into()?;
71            Ok(env.obj_to_i64(obj).map_err(Into::into)?)
72        }
73    }
74}
75
76impl<E: Env> TryFromVal<E, i64> for Val {
77    type Error = crate::Error;
78
79    fn try_from_val(env: &E, v: &i64) -> Result<Self, Self::Error> {
80        let v = *v;
81        if let Ok(so) = I64Small::try_from(v) {
82            Ok(so.into())
83        } else {
84            Ok(env.obj_from_i64(v).map_err(Into::into)?.to_val())
85        }
86    }
87}
88
89impl<E: Env> TryFromVal<E, &i64> for Val {
90    type Error = crate::Error;
91
92    fn try_from_val(env: &E, v: &&i64) -> Result<Self, Self::Error> {
93        Self::try_from_val(env, *v)
94    }
95}
96
97// u64 conversions
98
99impl<E: Env> TryFromVal<E, Val> for u64 {
100    type Error = crate::Error;
101
102    fn try_from_val(env: &E, val: &Val) -> Result<Self, Self::Error> {
103        let val = *val;
104        if let Ok(so) = U64Small::try_from(val) {
105            Ok(so.into())
106        } else {
107            let obj = val.try_into()?;
108            Ok(env.obj_to_u64(obj).map_err(Into::into)?)
109        }
110    }
111}
112
113impl<E: Env> TryFromVal<E, u64> for Val {
114    type Error = crate::Error;
115
116    fn try_from_val(env: &E, v: &u64) -> Result<Self, Self::Error> {
117        Ok(U64Val::try_from_val(env, v)?.to_val())
118    }
119}
120
121impl<E: Env> TryFromVal<E, &u64> for Val {
122    type Error = crate::Error;
123
124    fn try_from_val(env: &E, v: &&u64) -> Result<Self, Self::Error> {
125        Self::try_from_val(env, *v)
126    }
127}
128
129impl<E: Env> TryFromVal<E, U64Val> for u64 {
130    type Error = crate::Error;
131
132    fn try_from_val(env: &E, val: &U64Val) -> Result<Self, Self::Error> {
133        let val = *val;
134        if let Ok(so) = U64Small::try_from(val) {
135            Ok(so.into())
136        } else {
137            let obj = val.try_into()?;
138            Ok(env.obj_to_u64(obj).map_err(Into::into)?)
139        }
140    }
141}
142
143impl<E: Env> TryFromVal<E, u64> for U64Val {
144    type Error = crate::Error;
145
146    fn try_from_val(env: &E, v: &u64) -> Result<Self, Self::Error> {
147        let v = *v;
148        if let Ok(so) = U64Small::try_from(v) {
149            Ok(so.into())
150        } else {
151            Ok(env.obj_from_u64(v).map_err(Into::into)?.into())
152        }
153    }
154}
155
156// {Timepoint, Duration} <-> u64 conversions
157
158impl<E: Env> TryFromVal<E, TimepointVal> for u64 {
159    type Error = crate::Error;
160
161    fn try_from_val(env: &E, val: &TimepointVal) -> Result<Self, Self::Error> {
162        let val = *val;
163        if let Ok(so) = TimepointSmall::try_from(val) {
164            Ok(so.into())
165        } else {
166            let obj = val.try_into()?;
167            Ok(env.timepoint_obj_to_u64(obj).map_err(Into::into)?)
168        }
169    }
170}
171
172impl<E: Env> TryFromVal<E, u64> for TimepointVal {
173    type Error = crate::Error;
174
175    fn try_from_val(env: &E, v: &u64) -> Result<Self, Self::Error> {
176        let v = *v;
177        if let Ok(so) = TimepointSmall::try_from(v) {
178            Ok(so.into())
179        } else {
180            Ok(env.timepoint_obj_from_u64(v).map_err(Into::into)?.into())
181        }
182    }
183}
184
185impl<E: Env> TryFromVal<E, DurationVal> for u64 {
186    type Error = crate::Error;
187
188    fn try_from_val(env: &E, val: &DurationVal) -> Result<Self, Self::Error> {
189        let val = *val;
190        if let Ok(so) = DurationSmall::try_from(val) {
191            Ok(so.into())
192        } else {
193            let obj = val.try_into()?;
194            Ok(env.duration_obj_to_u64(obj).map_err(Into::into)?)
195        }
196    }
197}
198
199impl<E: Env> TryFromVal<E, u64> for DurationVal {
200    type Error = crate::Error;
201
202    fn try_from_val(env: &E, v: &u64) -> Result<Self, Self::Error> {
203        let v = *v;
204        if let Ok(so) = DurationSmall::try_from(v) {
205            Ok(so.into())
206        } else {
207            Ok(env.duration_obj_from_u64(v).map_err(Into::into)?.into())
208        }
209    }
210}
211
212// i128 conversions
213
214impl<E: Env> TryFromVal<E, Val> for i128 {
215    type Error = crate::Error;
216
217    fn try_from_val(env: &E, v: &Val) -> Result<Self, Self::Error> {
218        let v = *v;
219        if let Ok(so) = I128Small::try_from(v) {
220            Ok(so.into())
221        } else {
222            let obj = v.try_into()?;
223            let hi = env.obj_to_i128_hi64(obj).map_err(Into::into)?;
224            let lo = env.obj_to_i128_lo64(obj).map_err(Into::into)?;
225            Ok(int128_helpers::i128_from_pieces(hi, lo))
226        }
227    }
228}
229
230impl<E: Env> TryFromVal<E, i128> for Val {
231    type Error = crate::Error;
232
233    fn try_from_val(env: &E, v: &i128) -> Result<Self, Self::Error> {
234        Ok(I128Val::try_from_val(env, v)?.to_val())
235    }
236}
237
238impl<E: Env> TryFromVal<E, &i128> for Val {
239    type Error = crate::Error;
240
241    fn try_from_val(env: &E, v: &&i128) -> Result<Self, Self::Error> {
242        Self::try_from_val(env, *v)
243    }
244}
245
246impl<E: Env> TryFromVal<E, i128> for I128Val {
247    type Error = crate::Error;
248
249    fn try_from_val(env: &E, v: &i128) -> Result<Self, Self::Error> {
250        let v = *v;
251        if let Ok(so) = I128Small::try_from(v) {
252            Ok(so.into())
253        } else {
254            Ok(env
255                .obj_from_i128_pieces(int128_helpers::i128_hi(v), int128_helpers::i128_lo(v))
256                .map_err(Into::into)?
257                .into())
258        }
259    }
260}
261
262// u128 conversions
263
264impl<E: Env> TryFromVal<E, Val> for u128 {
265    type Error = crate::Error;
266
267    fn try_from_val(env: &E, v: &Val) -> Result<Self, Self::Error> {
268        let v = *v;
269        if let Ok(so) = U128Small::try_from(v) {
270            Ok(so.into())
271        } else {
272            let obj = v.try_into()?;
273            let hi = env.obj_to_u128_hi64(obj).map_err(Into::into)?;
274            let lo = env.obj_to_u128_lo64(obj).map_err(Into::into)?;
275            Ok(int128_helpers::u128_from_pieces(hi, lo))
276        }
277    }
278}
279
280impl<E: Env> TryFromVal<E, u128> for Val {
281    type Error = crate::Error;
282
283    fn try_from_val(env: &E, v: &u128) -> Result<Self, Self::Error> {
284        Ok(U128Val::try_from_val(env, v)?.to_val())
285    }
286}
287
288impl<E: Env> TryFromVal<E, &u128> for Val {
289    type Error = crate::Error;
290
291    fn try_from_val(env: &E, v: &&u128) -> Result<Self, Self::Error> {
292        Self::try_from_val(env, *v)
293    }
294}
295
296impl<E: Env> TryFromVal<E, u128> for U128Val {
297    type Error = crate::Error;
298
299    fn try_from_val(env: &E, v: &u128) -> Result<Self, Self::Error> {
300        let v = *v;
301        if let Ok(so) = U128Small::try_from(v) {
302            Ok(so.into())
303        } else {
304            Ok(env
305                .obj_from_u128_pieces(int128_helpers::u128_hi(v), int128_helpers::u128_lo(v))
306                .map_err(Into::into)?
307                .into())
308        }
309    }
310}
311
312// i256 conversions
313impl<E: Env> TryFromVal<E, Val> for I256 {
314    type Error = crate::Error;
315
316    fn try_from_val(env: &E, v: &Val) -> Result<Self, Self::Error> {
317        let v = *v;
318        if let Ok(so) = I256Small::try_from(v) {
319            Ok(so.into())
320        } else {
321            let obj = v.try_into()?;
322            let hi_hi = env.obj_to_i256_hi_hi(obj).map_err(Into::into)?;
323            let hi_lo = env.obj_to_i256_hi_lo(obj).map_err(Into::into)?;
324            let lo_hi = env.obj_to_i256_lo_hi(obj).map_err(Into::into)?;
325            let lo_lo = env.obj_to_i256_lo_lo(obj).map_err(Into::into)?;
326            Ok(i256_from_pieces(hi_hi, hi_lo, lo_hi, lo_lo))
327        }
328    }
329}
330
331impl<E: Env> TryFromVal<E, I256> for Val {
332    type Error = crate::Error;
333
334    fn try_from_val(env: &E, v: &I256) -> Result<Self, Self::Error> {
335        Ok(I256Val::try_from_val(env, v)?.to_val())
336    }
337}
338
339impl<E: Env> TryFromVal<E, &I256> for Val {
340    type Error = crate::Error;
341
342    fn try_from_val(env: &E, v: &&I256) -> Result<Self, Self::Error> {
343        Self::try_from_val(env, *v)
344    }
345}
346
347impl<E: Env> TryFromVal<E, I256> for I256Val {
348    type Error = crate::Error;
349
350    fn try_from_val(env: &E, v: &I256) -> Result<Self, Self::Error> {
351        let v = *v;
352        if let Ok(so) = I256Small::try_from(v) {
353            Ok(so.into())
354        } else {
355            let (hi_hi, hi_lo, lo_hi, lo_lo) = i256_into_pieces(v);
356            Ok(env
357                .obj_from_i256_pieces(hi_hi, hi_lo, lo_hi, lo_lo)
358                .map_err(Into::into)?
359                .into())
360        }
361    }
362}
363
364// u256 conversions
365impl<E: Env> TryFromVal<E, Val> for U256 {
366    type Error = crate::Error;
367
368    fn try_from_val(env: &E, v: &Val) -> Result<Self, Self::Error> {
369        let v = *v;
370        if let Ok(so) = U256Small::try_from(v) {
371            Ok(so.into())
372        } else {
373            let obj = v.try_into()?;
374            let hi_hi = env.obj_to_u256_hi_hi(obj).map_err(Into::into)?;
375            let hi_lo = env.obj_to_u256_hi_lo(obj).map_err(Into::into)?;
376            let lo_hi = env.obj_to_u256_lo_hi(obj).map_err(Into::into)?;
377            let lo_lo = env.obj_to_u256_lo_lo(obj).map_err(Into::into)?;
378            Ok(u256_from_pieces(hi_hi, hi_lo, lo_hi, lo_lo))
379        }
380    }
381}
382
383impl<E: Env> TryFromVal<E, U256> for Val {
384    type Error = crate::Error;
385
386    fn try_from_val(env: &E, v: &U256) -> Result<Self, Self::Error> {
387        Ok(U256Val::try_from_val(env, v)?.to_val())
388    }
389}
390
391impl<E: Env> TryFromVal<E, &U256> for Val {
392    type Error = crate::Error;
393
394    fn try_from_val(env: &E, v: &&U256) -> Result<Self, Self::Error> {
395        Self::try_from_val(env, *v)
396    }
397}
398
399impl<E: Env> TryFromVal<E, U256> for U256Val {
400    type Error = crate::Error;
401
402    fn try_from_val(env: &E, v: &U256) -> Result<Self, Self::Error> {
403        let v = *v;
404        if let Ok(so) = U256Small::try_from(v) {
405            Ok(so.into())
406        } else {
407            let (hi_hi, hi_lo, lo_hi, lo_lo) = u256_into_pieces(v);
408            Ok(env
409                .obj_from_u256_pieces(hi_hi, hi_lo, lo_hi, lo_lo)
410                .map_err(Into::into)?
411                .into())
412        }
413    }
414}
415
416// ScVal conversions (that require Object conversions)
417
418#[cfg(feature = "std")]
419impl<E: Env> TryFromVal<E, Val> for ScVal
420where
421    ScValObject: TryFromVal<E, Object>,
422    <ScValObject as TryFromVal<E, Object>>::Error: Into<crate::Error>,
423{
424    type Error = crate::Error;
425
426    fn try_from_val(env: &E, val: &Val) -> Result<Self, crate::Error> {
427        if let Ok(object) = Object::try_from(val) {
428            let scvo: ScValObject = object.try_into_val(env).map_err(|e| {
429                let e: <ScValObject as TryFromVal<E, Object>>::Error = e;
430                let e: crate::Error = e.into();
431                e
432            })?;
433            return Ok(scvo.into());
434        }
435        let val = *val;
436        match val.get_tag() {
437            Tag::False => Ok(ScVal::Bool(false)),
438            Tag::True => Ok(ScVal::Bool(true)),
439            Tag::Void => Ok(ScVal::Void),
440            Tag::Error => {
441                let error: Error = unsafe { <Error as ValConvert>::unchecked_from_val(val) };
442                Ok(error.try_into()?)
443            }
444            Tag::U32Val => Ok(ScVal::U32(val.get_major())),
445            Tag::I32Val => Ok(ScVal::I32(val.get_major() as i32)),
446            Tag::U64Small => Ok(ScVal::U64(val.get_body())),
447            Tag::I64Small => Ok(ScVal::I64(val.get_signed_body())),
448            Tag::TimepointSmall => Ok(ScVal::Timepoint(TimePoint(val.get_body()))),
449            Tag::DurationSmall => Ok(ScVal::Duration(Duration(val.get_body()))),
450            Tag::U128Small => Ok(ScVal::U128(UInt128Parts {
451                hi: 0,
452                lo: val.get_body(),
453            })),
454            Tag::I128Small => {
455                let body = val.get_signed_body() as i128;
456                Ok(ScVal::I128(Int128Parts {
457                    hi: (body >> 64) as i64,
458                    lo: body as u64,
459                }))
460            }
461            Tag::U256Small => Ok(ScVal::U256(UInt256Parts {
462                hi_hi: 0,
463                hi_lo: 0,
464                lo_hi: 0,
465                lo_lo: val.get_body(),
466            })),
467            Tag::I256Small => {
468                let body = val.get_signed_body() as i128;
469                let (hi_hi, hi_lo, lo_hi, lo_lo) = i256_into_pieces(I256::from(body));
470                Ok(ScVal::I256(Int256Parts {
471                    hi_hi,
472                    hi_lo,
473                    lo_hi,
474                    lo_lo,
475                }))
476            }
477            Tag::SymbolSmall => {
478                let sym: SymbolSmall =
479                    unsafe { <SymbolSmall as ValConvert>::unchecked_from_val(val) };
480                let str: String = sym.into_iter().collect();
481                Ok(ScVal::Symbol(crate::xdr::ScSymbol(
482                    str.as_bytes().try_into()?,
483                )))
484            }
485
486            // The object types should all be handled above, and the other tag
487            // cases should never occur.
488            Tag::U64Object
489            | Tag::I64Object
490            | Tag::TimepointObject
491            | Tag::DurationObject
492            | Tag::U128Object
493            | Tag::I128Object
494            | Tag::U256Object
495            | Tag::I256Object
496            | Tag::BytesObject
497            | Tag::StringObject
498            | Tag::SymbolObject
499            | Tag::VecObject
500            | Tag::MapObject
501            | Tag::AddressObject
502            | Tag::SmallCodeUpperBound
503            | Tag::ObjectCodeLowerBound
504            | Tag::ObjectCodeUpperBound
505            | Tag::Bad => Err(ConversionError.into()),
506        }
507    }
508}
509
510#[cfg(feature = "std")]
511fn require_or_conversion_error(b: bool) -> Result<(), ConversionError> {
512    if !b {
513        Err(ConversionError)
514    } else {
515        Ok(())
516    }
517}
518
519#[cfg(feature = "std")]
520impl<E: Env> TryFromVal<E, ScVal> for Val
521where
522    Object: for<'a> TryFromVal<E, ScValObjRef<'a>>,
523    for<'a> <Object as TryFromVal<E, ScValObjRef<'a>>>::Error: Into<crate::Error>,
524{
525    type Error = crate::Error;
526    fn try_from_val(env: &E, val: &ScVal) -> Result<Val, Self::Error> {
527        if !Val::can_represent_scval(val) {
528            return Err(ConversionError.into());
529        }
530
531        if let Some(scvo) = ScValObjRef::classify(val) {
532            let obj = Object::try_from_val(env, &scvo).map_err(|e| {
533                let e: <Object as TryFromVal<E, ScValObjRef>>::Error = e;
534                let e: crate::Error = e.into();
535                e
536            })?;
537            return Ok(obj.into());
538        }
539
540        // Remaining cases should only be "small" types.
541        Ok(match val {
542            ScVal::Bool(b) => Val::from_bool(*b).into(),
543            ScVal::Void => Val::from_void().into(),
544            ScVal::Error(e) => e.into(),
545            ScVal::U32(u) => (*u).into(),
546            ScVal::I32(i) => (*i).into(),
547            ScVal::U64(u) => {
548                require_or_conversion_error(num::is_small_u64(*u))?;
549                unsafe { Val::from_body_and_tag(*u, Tag::U64Small) }
550            }
551            ScVal::I64(i) => {
552                require_or_conversion_error(num::is_small_i64(*i))?;
553                unsafe { Val::from_body_and_tag(*i as u64, Tag::I64Small) }
554            }
555            ScVal::Timepoint(TimePoint(u)) => {
556                require_or_conversion_error(num::is_small_u64(*u))?;
557                unsafe { Val::from_body_and_tag(*u, Tag::TimepointSmall) }
558            }
559            ScVal::Duration(Duration(u)) => {
560                require_or_conversion_error(num::is_small_u64(*u))?;
561                unsafe { Val::from_body_and_tag(*u, Tag::DurationSmall) }
562            }
563            ScVal::U128(u) => {
564                let u: u128 = u.into();
565                require_or_conversion_error(num::is_small_u128(u))?;
566                unsafe { Val::from_body_and_tag(u as u64, Tag::U128Small) }
567            }
568            ScVal::I128(i) => {
569                let i: i128 = i.into();
570                require_or_conversion_error(num::is_small_i128(i))?;
571                unsafe { Val::from_body_and_tag((i as i64) as u64, Tag::I128Small) }
572            }
573            ScVal::U256(u) => {
574                require_or_conversion_error(num::is_small_u256_parts(u))?;
575                unsafe { Val::from_body_and_tag(u.lo_lo, Tag::U256Small) }
576            }
577            ScVal::I256(i) => {
578                require_or_conversion_error(num::is_small_i256_parts(i))?;
579                unsafe { Val::from_body_and_tag(i.lo_lo, Tag::I256Small) }
580            }
581            ScVal::Symbol(bytes) => {
582                // NB: Long symbols are objects and should have been
583                // handled before reaching this point.
584                SymbolSmall::try_from_bytes(bytes.as_slice())?.into()
585            }
586
587            // These should all have been classified as ScValObjRef above, or are
588            // reserved ScVal types Val::can_represent_scval would have returned
589            // false from above.
590            ScVal::Bytes(_)
591            | ScVal::String(_)
592            | ScVal::Vec(_)
593            | ScVal::Map(_)
594            | ScVal::Address(_)
595            | ScVal::LedgerKeyNonce(_)
596            | ScVal::LedgerKeyContractInstance
597            | ScVal::ContractInstance(_) => return Err(ConversionError.into()),
598        })
599    }
600}
601
602#[cfg(feature = "std")]
603impl<E: Env> TryFromVal<E, &ScVal> for Val
604where
605    Object: for<'a> TryFromVal<E, ScValObjRef<'a>>,
606    for<'a> <Object as TryFromVal<E, ScValObjRef<'a>>>::Error: Into<crate::Error>,
607{
608    type Error = crate::Error;
609    fn try_from_val(env: &E, val: &&ScVal) -> Result<Val, Self::Error> {
610        Self::try_from_val(env, *val)
611    }
612}