Skip to main content

amaru_kernel/utils/
string.rs

1// Copyright 2025 PRAGMA
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt::{Debug, Display};
16
17use itertools::Itertools;
18
19use crate::{Bytes, cbor, from_cbor};
20
21pub fn encode_bech32(hrp: &str, payload: &[u8]) -> Result<String, Box<dyn std::error::Error>> {
22    let hrp = bech32::Hrp::parse(hrp)?;
23    Ok(bech32::encode::<bech32::Bech32>(hrp, payload)?)
24}
25
26pub fn display_collection<T>(collection: impl IntoIterator<Item = T>) -> String
27where
28    T: std::fmt::Display,
29{
30    collection.into_iter().collect::<Vec<_>>().list_to_string(", ")
31}
32
33/// Extension trait to convert a list of displayable items into a single string.
34/// For example, `vec![1, 2, 3].list_to_string(", ")` will produce the string `1, 2, 3`.
35pub trait ListToString {
36    fn list_to_string(&self, separator: &str) -> String;
37}
38
39impl<I, H> ListToString for I
40where
41    for<'a> &'a I: IntoIterator<Item = &'a H>,
42    H: Display,
43{
44    fn list_to_string(&self, separator: &str) -> String {
45        self.into_iter().join(separator)
46    }
47}
48
49/// Extension trait to convert a list of lists of displayable items into a single string.
50/// For example, `vec![vec![1, 2], vec![3]].lists_to_string(", ", " | ")` will produce
51/// the string `"[1, 2] | [3]"`.
52pub trait ListsToString {
53    fn lists_to_string(&self, intra_separator: &str, inter_separator: &str) -> String;
54}
55
56impl<H, I, J> ListsToString for J
57where
58    for<'a> &'a I: IntoIterator<Item = &'a H>,
59    for<'a> &'a J: IntoIterator<Item = &'a I>,
60    H: Display,
61{
62    fn lists_to_string(&self, intra_separator: &str, inter_separator: &str) -> String {
63        self.into_iter()
64            .map(|l| format!("[{}]", l.list_to_string(intra_separator)))
65            .collect::<Vec<_>>()
66            .list_to_string(inter_separator)
67    }
68}
69
70/// Extension trait to convert a list of debug-printable items into a single string.
71/// For example, `vec![Some(1), None, Some(3)].list_debug(", ")` will produce
72/// the string `Some(1), None, Some(3)`
73pub trait ListDebug {
74    fn list_debug(&self, separator: &str) -> String;
75}
76
77impl<I, H> ListDebug for I
78where
79    for<'a> &'a I: IntoIterator<Item = &'a H>,
80    H: Debug,
81{
82    fn list_debug(&self, separator: &str) -> String {
83        self.into_iter().map(|h| format!("{h:?}")).join(separator)
84    }
85}
86
87/// Extension trait to convert a list of lists of debuggable items into a single string.
88/// For example, `vec![vec![Some(1), Some(2)], vec![Some(3)]].lists_debug(", ", " | ")` will produce
89/// the string `"[Some(1), Some(2)] | [Some(3)]"`.
90pub trait ListsDebug {
91    fn lists_debug(&self, intra_separator: &str, inter_separator: &str) -> String;
92}
93
94impl<H, I, J> ListsDebug for J
95where
96    for<'a> &'a I: IntoIterator<Item = &'a H>,
97    for<'a> &'a J: IntoIterator<Item = &'a I>,
98    H: Debug,
99{
100    fn lists_debug(&self, intra_separator: &str, inter_separator: &str) -> String {
101        self.into_iter()
102            .map(|l| format!("[{}]", l.list_debug(intra_separator)))
103            .collect::<Vec<_>>()
104            .list_to_string(inter_separator)
105    }
106}
107
108/// An implementation of 'TryFrom' from hex-encoded strings, for any type that can be decoded from
109/// CBOR. Yields the original bytes and the deserialized value.
110pub fn blanket_try_from_hex_bytes<T, I: for<'d> cbor::Decode<'d, ()>>(
111    s: &str,
112    new: impl Fn(Bytes, I) -> T,
113) -> Result<T, String> {
114    let original_bytes = Bytes::from(hex::decode(s.as_bytes()).map_err(|e| e.to_string())?);
115
116    let value = from_cbor(&original_bytes).ok_or_else(|| "failed to decode from CBOR".to_string())?;
117
118    Ok(new(original_bytes, value))
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn make_a_string_from_a_list() {
127        let actual = vec![1, 2, 3].list_to_string(", ");
128        assert_eq!(actual, "1, 2, 3");
129    }
130
131    #[test]
132    fn make_a_debug_string_from_a_list() {
133        let actual = vec![Some(1), None, Some(3)].list_debug(", ");
134        assert_eq!(actual, "Some(1), None, Some(3)");
135    }
136
137    #[test]
138    fn make_a_string_from_a_list_of_lists() {
139        let actual = vec![vec![1, 2], vec![3]].lists_to_string(", ", " | ");
140        assert_eq!(actual, "[1, 2] | [3]");
141    }
142}