stellar_xdr/next/
str.rs

1//# Custom string representations of the following types, also used for JSON
2//# formatting.
3//#
4//# The types that has impls in this file are given to the xdrgen
5//# --rust-types-custom-str-impl cli option, so that xdrgen does not generate
6//# FromStr and Display impls for them.
7//#
8//# ## Strkey Types (SEP-23)
9//# - PublicKey
10//# - AccountId
11//# - MuxedAccount
12//# - MuxedAccountMed25519
13//# - SignerKey
14//# - SignerKeyEd25519SignedPayload
15//# - NodeId
16//#
17//# ## Asset Codes
18//# - AssetCode
19//# - AssetCode4
20//# - AssetCode12
21//#
22//# ## Other
23//# - ClaimableBalanceId
24#![cfg(feature = "alloc")]
25
26use super::{
27    AccountId, AssetCode, AssetCode12, AssetCode4, ClaimableBalanceId, Error, Hash, MuxedAccount,
28    MuxedAccountMed25519, NodeId, PublicKey, ScAddress, SignerKey, SignerKeyEd25519SignedPayload,
29    Uint256,
30};
31
32impl From<stellar_strkey::DecodeError> for Error {
33    fn from(_: stellar_strkey::DecodeError) -> Self {
34        Error::Invalid
35    }
36}
37
38impl core::fmt::Display for PublicKey {
39    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
40        match self {
41            PublicKey::PublicKeyTypeEd25519(Uint256(k)) => {
42                let k = stellar_strkey::ed25519::PublicKey::from_payload(k)
43                    .map_err(|_| core::fmt::Error)?;
44                let s = k.to_string();
45                f.write_str(&s)?;
46            }
47        }
48        Ok(())
49    }
50}
51
52impl core::str::FromStr for PublicKey {
53    type Err = Error;
54    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
55        let stellar_strkey::ed25519::PublicKey(k) =
56            stellar_strkey::ed25519::PublicKey::from_str(s)?;
57        Ok(PublicKey::PublicKeyTypeEd25519(Uint256(k)))
58    }
59}
60
61impl core::fmt::Display for AccountId {
62    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
63        self.0.fmt(f)
64    }
65}
66
67impl core::str::FromStr for AccountId {
68    type Err = Error;
69    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
70        Ok(AccountId(PublicKey::from_str(s)?))
71    }
72}
73
74impl core::fmt::Display for MuxedAccountMed25519 {
75    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
76        let MuxedAccountMed25519 {
77            ed25519: Uint256(ed25519),
78            id,
79        } = self;
80        let k = stellar_strkey::ed25519::MuxedAccount {
81            ed25519: *ed25519,
82            id: *id,
83        };
84        let s = k.to_string();
85        f.write_str(&s)?;
86        Ok(())
87    }
88}
89
90impl core::str::FromStr for MuxedAccountMed25519 {
91    type Err = Error;
92    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
93        let stellar_strkey::ed25519::MuxedAccount { ed25519, id } =
94            stellar_strkey::ed25519::MuxedAccount::from_str(s)?;
95        Ok(MuxedAccountMed25519 {
96            ed25519: Uint256(ed25519),
97            id,
98        })
99    }
100}
101
102impl core::str::FromStr for MuxedAccount {
103    type Err = Error;
104    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
105        let strkey = stellar_strkey::Strkey::from_str(s)?;
106        match strkey {
107            stellar_strkey::Strkey::PublicKeyEd25519(stellar_strkey::ed25519::PublicKey(k)) => {
108                Ok(MuxedAccount::Ed25519(Uint256(k)))
109            }
110            stellar_strkey::Strkey::MuxedAccountEd25519(
111                stellar_strkey::ed25519::MuxedAccount { ed25519, id },
112            ) => Ok(MuxedAccount::MuxedEd25519(MuxedAccountMed25519 {
113                ed25519: Uint256(ed25519),
114                id,
115            })),
116            stellar_strkey::Strkey::PrivateKeyEd25519(_)
117            | stellar_strkey::Strkey::PreAuthTx(_)
118            | stellar_strkey::Strkey::HashX(_)
119            | stellar_strkey::Strkey::SignedPayloadEd25519(_)
120            | stellar_strkey::Strkey::Contract(_) => Err(Error::Invalid),
121        }
122    }
123}
124
125impl core::fmt::Display for MuxedAccount {
126    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
127        match self {
128            MuxedAccount::Ed25519(Uint256(k)) => {
129                let k = stellar_strkey::ed25519::PublicKey(*k);
130                let s = k.to_string();
131                f.write_str(&s)?;
132            }
133            MuxedAccount::MuxedEd25519(m) => m.fmt(f)?,
134        }
135        Ok(())
136    }
137}
138
139impl core::fmt::Display for NodeId {
140    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
141        self.0.fmt(f)
142    }
143}
144
145impl core::str::FromStr for NodeId {
146    type Err = Error;
147    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
148        Ok(NodeId(PublicKey::from_str(s)?))
149    }
150}
151
152impl core::fmt::Display for SignerKeyEd25519SignedPayload {
153    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
154        let SignerKeyEd25519SignedPayload {
155            ed25519: Uint256(ed25519),
156            payload,
157        } = self;
158        let k = stellar_strkey::ed25519::SignedPayload {
159            ed25519: *ed25519,
160            payload: payload.into(),
161        };
162        let s = k.to_string();
163        f.write_str(&s)?;
164        Ok(())
165    }
166}
167
168impl core::str::FromStr for SignerKeyEd25519SignedPayload {
169    type Err = Error;
170    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
171        let stellar_strkey::ed25519::SignedPayload { ed25519, payload } =
172            stellar_strkey::ed25519::SignedPayload::from_str(s)?;
173        Ok(SignerKeyEd25519SignedPayload {
174            ed25519: Uint256(ed25519),
175            payload: payload.try_into()?,
176        })
177    }
178}
179
180impl core::str::FromStr for SignerKey {
181    type Err = Error;
182    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
183        let strkey = stellar_strkey::Strkey::from_str(s)?;
184        match strkey {
185            stellar_strkey::Strkey::PublicKeyEd25519(stellar_strkey::ed25519::PublicKey(k)) => {
186                Ok(SignerKey::Ed25519(Uint256(k)))
187            }
188            stellar_strkey::Strkey::PreAuthTx(stellar_strkey::PreAuthTx(h)) => {
189                Ok(SignerKey::PreAuthTx(Uint256(h)))
190            }
191            stellar_strkey::Strkey::HashX(stellar_strkey::HashX(h)) => {
192                Ok(SignerKey::HashX(Uint256(h)))
193            }
194            stellar_strkey::Strkey::SignedPayloadEd25519(
195                stellar_strkey::ed25519::SignedPayload { ed25519, payload },
196            ) => Ok(SignerKey::Ed25519SignedPayload(
197                SignerKeyEd25519SignedPayload {
198                    ed25519: Uint256(ed25519),
199                    payload: payload.try_into()?,
200                },
201            )),
202            stellar_strkey::Strkey::PrivateKeyEd25519(_)
203            | stellar_strkey::Strkey::Contract(_)
204            | stellar_strkey::Strkey::MuxedAccountEd25519(_) => Err(Error::Invalid),
205        }
206    }
207}
208
209impl core::fmt::Display for SignerKey {
210    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
211        match self {
212            SignerKey::Ed25519(Uint256(k)) => {
213                let k = stellar_strkey::ed25519::PublicKey(*k);
214                let s = k.to_string();
215                f.write_str(&s)?;
216            }
217            SignerKey::PreAuthTx(Uint256(h)) => {
218                let k = stellar_strkey::PreAuthTx(*h);
219                let s = k.to_string();
220                f.write_str(&s)?;
221            }
222            SignerKey::HashX(Uint256(h)) => {
223                let k = stellar_strkey::HashX(*h);
224                let s = k.to_string();
225                f.write_str(&s)?;
226            }
227            SignerKey::Ed25519SignedPayload(p) => p.fmt(f)?,
228        }
229        Ok(())
230    }
231}
232
233impl core::str::FromStr for ScAddress {
234    type Err = Error;
235    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
236        let strkey = stellar_strkey::Strkey::from_str(s)?;
237        match strkey {
238            stellar_strkey::Strkey::PublicKeyEd25519(stellar_strkey::ed25519::PublicKey(k)) => Ok(
239                ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(k)))),
240            ),
241            stellar_strkey::Strkey::Contract(stellar_strkey::Contract(h)) => {
242                Ok(ScAddress::Contract(Hash(h)))
243            }
244            stellar_strkey::Strkey::MuxedAccountEd25519(_)
245            | stellar_strkey::Strkey::PrivateKeyEd25519(_)
246            | stellar_strkey::Strkey::PreAuthTx(_)
247            | stellar_strkey::Strkey::HashX(_)
248            | stellar_strkey::Strkey::SignedPayloadEd25519(_) => Err(Error::Invalid),
249        }
250    }
251}
252
253impl core::fmt::Display for ScAddress {
254    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
255        match self {
256            ScAddress::Account(a) => a.fmt(f)?,
257            ScAddress::Contract(Hash(h)) => {
258                let k = stellar_strkey::Contract(*h);
259                let s = k.to_string();
260                f.write_str(&s)?;
261            }
262        }
263        Ok(())
264    }
265}
266
267impl core::str::FromStr for AssetCode4 {
268    type Err = Error;
269    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
270        let mut code = AssetCode4([0u8; 4]);
271        escape_bytes::unescape_into(&mut code.0, s.as_bytes()).map_err(|_| Error::Invalid)?;
272        Ok(code)
273    }
274}
275
276impl core::fmt::Display for AssetCode4 {
277    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
278        if let Some(last_idx) = self.0.iter().rposition(|c| *c != 0) {
279            for b in escape_bytes::Escape::new(&self.0[..=last_idx]) {
280                write!(f, "{}", b as char)?;
281            }
282        }
283        Ok(())
284    }
285}
286
287impl core::str::FromStr for AssetCode12 {
288    type Err = Error;
289    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
290        let mut code = AssetCode12([0u8; 12]);
291        escape_bytes::unescape_into(&mut code.0, s.as_bytes()).map_err(|_| Error::Invalid)?;
292        Ok(code)
293    }
294}
295
296impl core::fmt::Display for AssetCode12 {
297    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
298        if let Some(last_idx) = self.0.iter().rposition(|c| *c != 0) {
299            for b in escape_bytes::Escape::new(&self.0[..=last_idx]) {
300                write!(f, "{}", b as char)?;
301            }
302        }
303        Ok(())
304    }
305}
306
307impl core::str::FromStr for AssetCode {
308    type Err = Error;
309    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
310        let mut code = [0u8; 12];
311        let n = escape_bytes::unescape_into(&mut code, s.as_bytes()).map_err(|_| Error::Invalid)?;
312        if n <= 4 {
313            Ok(AssetCode::CreditAlphanum4(AssetCode4([
314                code[0], code[1], code[2], code[3],
315            ])))
316        } else if n <= 12 {
317            Ok(AssetCode::CreditAlphanum12(AssetCode12(code)))
318        } else {
319            Err(Error::Invalid)
320        }
321    }
322}
323
324impl core::fmt::Display for AssetCode {
325    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
326        match self {
327            AssetCode::CreditAlphanum4(c) => c.fmt(f),
328            AssetCode::CreditAlphanum12(c) => c.fmt(f),
329        }
330    }
331}
332
333impl core::str::FromStr for ClaimableBalanceId {
334    type Err = Error;
335    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
336        // This conversion to a hex string could be done by XDR encoding the
337        // self value, but because XDR encoding requires the std feature, this
338        // approach is taken instead to preserve the fact that the serde feature
339        // is available with alloc only.
340        let bytes = hex::decode(s).map_err(|_| Error::InvalidHex)?;
341        match bytes.as_slice() {
342            [0, 0, 0, 0, ..] => Ok(ClaimableBalanceId::ClaimableBalanceIdTypeV0(Hash(
343                (&bytes[4..]).try_into()?,
344            ))),
345            _ => Err(Error::Invalid),
346        }
347    }
348}
349
350impl core::fmt::Display for ClaimableBalanceId {
351    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
352        // This conversion from a hex string could be done by XDR decoding the
353        // self value, but because XDR decoding requires the std feature, this
354        // approach is taken instead to preserve the fact that the serde feature
355        // is available with alloc only.
356        match self {
357            ClaimableBalanceId::ClaimableBalanceIdTypeV0(Hash(bytes)) => {
358                for b in [0u8, 0, 0, 0] {
359                    write!(f, "{b:02x}")?;
360                }
361                for b in bytes {
362                    write!(f, "{b:02x}")?;
363                }
364            }
365        }
366        Ok(())
367    }
368}