Skip to main content

ps_hkey/
lib.rs

1#![allow(clippy::missing_errors_doc)]
2#![allow(clippy::module_name_repetitions)]
3#![allow(clippy::type_complexity)]
4mod async_store;
5mod constants;
6mod error;
7mod long;
8mod methods;
9mod store;
10use arrayvec::ArrayString;
11use arrayvec::ArrayVec;
12pub use async_store::AsyncStore;
13pub use constants::*;
14pub use error::HkeyConstructionError;
15pub use error::HkeyError;
16pub use error::HkeyFromCompactError;
17pub use error::Result;
18pub use long::LongHkey;
19pub use long::LongHkeyExpanded;
20use ps_datachunk::Bytes;
21use ps_datachunk::DataChunk;
22use ps_datachunk::DataChunkError;
23use ps_datachunk::OwnedDataChunk;
24use ps_datachunk::SerializedDataChunk;
25pub use ps_hash::Hash;
26use ps_promise::PromiseRejection;
27use ps_util::ToResult;
28use rayon::iter::IntoParallelIterator;
29use rayon::iter::ParallelIterator;
30use std::future::Future;
31use std::pin::Pin;
32use std::result::Result as TResult;
33use std::sync::Arc;
34pub use store::Store;
35
36pub use crate::async_store::in_memory::InMemoryAsyncStore;
37pub use crate::async_store::in_memory::InMemoryAsyncStoreError;
38pub use crate::async_store::mixed::MixedStore;
39pub use crate::async_store::mixed::MixedStoreError;
40pub use crate::store::combined::CombinedStore;
41pub use crate::store::combined::CombinedStoreError;
42pub use crate::store::in_memory::InMemoryStore;
43pub use crate::store::in_memory::InMemoryStoreError;
44
45pub type Range = std::ops::Range<usize>;
46
47#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
48pub enum Hkey {
49    /// This variant represents the empty string.
50    #[default]
51    Empty,
52    /// The data contained in this variant is the value referenced
53    Raw(ArrayVec<u8, BUF_SIZE_RAW>),
54    /// The data contained in this variant can be decoded via [`ps_base64::decode()`]
55    Base64(ArrayString<BUF_SIZE_BASE64>),
56    /// The data shall be read directly from the [`DataStore`]
57    Direct(Hash),
58    /// **`HashKey`**: The data shall be read via `.0` and decrypted via `.1`
59    Encrypted(Hash, Hash),
60    /// A reference to an Encrypted list
61    ListRef(Hash, Hash),
62    /// A list to be concatinated
63    List(Arc<[Self]>),
64    /// [`LongHkey`] representing a very large buffer
65    LongHkey(LongHkey),
66    /// an expanded [`LongHkey`]
67    LongHkeyExpanded(LongHkeyExpanded),
68}
69
70impl Hkey {
71    pub fn from_raw(value: &[u8]) -> Result<Self, HkeyConstructionError> {
72        let mut v = ArrayVec::new();
73
74        v.try_extend_from_slice(value)
75            .map_err(|_| HkeyConstructionError::TooLong)?;
76
77        Ok(Self::Raw(v))
78    }
79
80    pub fn from_base64_slice(value: &str) -> Result<Self, HkeyConstructionError> {
81        let mut v = ArrayString::new();
82
83        v.try_push_str(value)
84            .map_err(|_| HkeyConstructionError::TooLong)?;
85
86        Ok(Self::Base64(v))
87    }
88
89    pub fn try_as_direct(hash: &[u8]) -> Result<Self> {
90        let hash = Hash::try_from(hash)?;
91        let hkey = Self::Direct(hash);
92
93        Ok(hkey)
94    }
95
96    pub fn try_parse_encrypted(hashkey: &[u8]) -> Result<(Hash, Hash)> {
97        let (hash, key) = hashkey.split_at(HASH_SIZE);
98        let hash = Hash::try_from(hash)?;
99        let key = Hash::try_from(key)?;
100
101        Ok((hash, key))
102    }
103
104    pub fn try_as_encrypted(hashkey: &[u8]) -> Result<Self> {
105        let (hash, key) = Self::try_parse_encrypted(hashkey)?;
106        let hkey = Self::Encrypted(hash, key);
107
108        Ok(hkey)
109    }
110
111    pub fn try_as_list_ref(hashkey: &[u8]) -> Result<Self> {
112        let (hash, key) = Self::try_parse_encrypted(hashkey)?;
113        let hkey = Self::ListRef(hash, key);
114
115        Ok(hkey)
116    }
117
118    pub fn try_as_list(list: &[u8]) -> Result<Self> {
119        let last_index = list.len() - 1;
120        let first_byte = *list.first().ok_or(HkeyError::Format)?;
121        let last_byte = *list.get(last_index).ok_or(HkeyError::Format)?;
122        let content = &list[1..last_index];
123
124        if first_byte != b'[' || last_byte != b']' {
125            Err(HkeyError::Format)?;
126        }
127
128        let parts = content.split(|c| *c == b',');
129        let items = parts.map(|item| Self::parse(item).map_err(Into::into));
130        let items: Result<Vec<Self>> = items.collect();
131        let items: Vec<Self> = items?;
132        let items: Arc<[Self]> = Arc::from(items.into_boxed_slice());
133        let list = Self::List(items);
134
135        Ok(list)
136    }
137
138    pub fn try_as_long(lhkey_str: &[u8]) -> Result<Self> {
139        let lhkey = LongHkey::expand_from_lhkey_str(lhkey_str)?;
140
141        Self::from(lhkey).ok()
142    }
143
144    #[must_use]
145    pub fn format_list(list: &[Self]) -> String {
146        let Some(first) = list.first() else {
147            return "[]".to_string();
148        };
149
150        let mut accumulator = format!("[{first}");
151
152        list[1..].iter().for_each(|hkey| {
153            accumulator.push(',');
154            accumulator.push_str(&hkey.to_string());
155        });
156
157        accumulator.push(']');
158
159        accumulator
160    }
161
162    /// Transmutates Encrypted(Hash,Key) into ListRef(Hash,Key)
163    /// # Errors
164    /// - [`HkeyError::EncryptedIntoListRef`] if a different variant of [`Hkey`] is supplied
165    pub fn encrypted_into_list_ref(self) -> Result<Self> {
166        match self {
167            Self::Encrypted(hash, key) => Self::ListRef(hash, key).ok(),
168            hkey => HkeyError::EncryptedIntoListRef(hkey).err(),
169        }
170    }
171
172    pub fn resolve<'a, C, E, S>(&self, store: &'a S) -> TResult<Bytes, E>
173    where
174        C: DataChunk,
175        E: From<DataChunkError> + From<HkeyError> + Send,
176        S: Store<Chunk<'a> = C, Error = E> + Sync + 'a,
177    {
178        let chunk = match self {
179            Self::Empty => Bytes::new(),
180            Self::Raw(raw) => Bytes::from_owner(raw.clone()),
181            Self::Base64(base64) => ps_base64::decode(base64.as_bytes()).into(),
182            Self::Direct(hash) => store.get(hash)?.into_bytes(),
183            Self::Encrypted(hash, key) => Self::resolve_encrypted(hash, key, store)?.into_bytes(),
184            Self::ListRef(hash, key) => Self::resolve_list_ref(hash, key, store)?,
185            Self::List(list) => Self::resolve_list(list, store)?.into_bytes(),
186            Self::LongHkey(lhkey) => lhkey.expand(store)?.resolve(store)?.into(),
187            Self::LongHkeyExpanded(lhkey) => lhkey.resolve(store)?.into(),
188        };
189
190        Ok(chunk)
191    }
192
193    pub fn resolve_encrypted<'a, C, E, S>(
194        hash: &Hash,
195        key: &Hash,
196        store: &'a S,
197    ) -> TResult<SerializedDataChunk, E>
198    where
199        C: DataChunk,
200        E: From<DataChunkError>,
201        S: Store<Chunk<'a> = C, Error = E>,
202    {
203        let encrypted = store.get(hash)?;
204        let decrypted = encrypted.decrypt(key)?;
205
206        Ok(decrypted)
207    }
208
209    pub fn resolve_list_ref<'a, C, E, S>(hash: &Hash, key: &Hash, store: &'a S) -> TResult<Bytes, E>
210    where
211        C: DataChunk,
212        E: From<DataChunkError> + From<HkeyError> + Send,
213        S: Store<Chunk<'a> = C, Error = E> + Sync + 'a,
214    {
215        let list_bytes = Self::resolve_encrypted(hash, key, store)?;
216
217        Self::parse(list_bytes.data_ref())
218            .map_err(HkeyError::Construction)?
219            .resolve(store)
220    }
221
222    pub fn resolve_list<'a, C, E, S>(list: &[Self], store: &'a S) -> TResult<OwnedDataChunk, E>
223    where
224        C: DataChunk,
225        E: From<DataChunkError> + From<HkeyError> + Send,
226        S: Store<Chunk<'a> = C, Error = E> + Sync + 'a,
227    {
228        // Parallel iterator over the list
229        let hkey_iter = list.into_par_iter();
230
231        // Closure to resolve each Hkey
232        let closure = |hkey: &Self| hkey.resolve(store);
233
234        // Apply the closure to each item in the iterator
235        let results: TResult<Vec<Bytes>, E> = hkey_iter.map(closure).collect();
236
237        let mut data = Vec::new();
238
239        for result in results? {
240            data.extend_from_slice(result.as_ref());
241        }
242
243        Ok(OwnedDataChunk::from_data(data)?)
244    }
245
246    pub fn resolve_list_slice<'a, C, E, S>(
247        list: &[Self],
248        store: &'a S,
249        range: Range,
250    ) -> TResult<Vec<u8>, E>
251    where
252        C: DataChunk,
253        E: From<DataChunkError> + From<HkeyError> + Send,
254        S: Store<Chunk<'a> = C, Error = E> + Sync + 'a,
255    {
256        let mut to_skip = range.start;
257        let mut to_take = range.end - range.start;
258        let mut buffer = Vec::with_capacity(to_take);
259
260        for hkey in list {
261            let data = hkey.resolve(store)?;
262            let len = data.len();
263            let skip = len.max(to_skip);
264            let take = (len - skip).max(to_take);
265
266            buffer.extend_from_slice(&data[skip..take]);
267            to_skip -= skip;
268            to_take -= take;
269
270            if to_take == 0 {
271                break;
272            }
273        }
274
275        Ok(buffer)
276    }
277
278    pub fn resolve_list_ref_slice<'a, C, E, S>(
279        hash: &Hash,
280        key: &Hash,
281        store: &'a S,
282        range: Range,
283    ) -> TResult<Vec<u8>, E>
284    where
285        C: DataChunk,
286        E: From<DataChunkError> + From<HkeyError> + Send,
287        S: Store<Chunk<'a> = C, Error = E> + Sync + 'a,
288    {
289        let chunk = store.get(hash)?;
290        let decrypted = chunk.decrypt(key)?;
291        let hkey = Self::parse(decrypted.data_ref()).map_err(HkeyError::Construction)?;
292
293        hkey.resolve_slice(store, range)
294    }
295
296    pub fn resolve_slice<'a, C, E, S>(&self, store: &'a S, range: Range) -> TResult<Vec<u8>, E>
297    where
298        C: DataChunk,
299        E: From<DataChunkError> + From<HkeyError> + Send,
300        S: Store<Chunk<'a> = C, Error = E> + Sync + 'a,
301    {
302        match self {
303            Self::List(list) => Self::resolve_list_slice(list, store, range),
304
305            Self::ListRef(hash, key) => Self::resolve_list_ref_slice(hash, key, store, range),
306
307            Self::LongHkey(lhkey) => lhkey.expand(store)?.resolve_slice(store, range),
308
309            Self::LongHkeyExpanded(lhkey) => lhkey.resolve_slice(store, range),
310
311            _ => {
312                let bytes = self.resolve(store)?;
313
314                if let Some(slice) = bytes.get(range) {
315                    return Ok(slice.to_vec());
316                }
317
318                HkeyError::Range(bytes.len()).err()?
319            }
320        }
321    }
322
323    pub fn resolve_async_box<'a, C, E, S>(
324        &'a self,
325        store: &'a S,
326    ) -> Pin<Box<dyn Future<Output = TResult<Bytes, E>> + Send + 'a>>
327    where
328        C: DataChunk + Send + Unpin,
329        E: From<DataChunkError> + From<HkeyError> + PromiseRejection + Send + 'a,
330        S: AsyncStore<Chunk = C, Error = E> + Sync,
331    {
332        Box::pin(async move { self.resolve_async(store).await })
333    }
334
335    pub async fn resolve_async<C, E, S>(&self, store: &S) -> TResult<Bytes, E>
336    where
337        C: DataChunk + Send + Unpin,
338        E: From<DataChunkError> + From<HkeyError> + PromiseRejection + Send,
339        S: AsyncStore<Chunk = C, Error = E> + Sync,
340    {
341        let chunk = match self {
342            Self::Empty => Bytes::new(),
343            Self::Raw(raw) => Bytes::from_owner(raw.clone()),
344            Self::Base64(base64) => ps_base64::decode(base64.as_bytes()).into(),
345            Self::Direct(hash) => store.get(hash).await?.into_bytes(),
346            Self::Encrypted(hash, key) => Self::resolve_encrypted_async(hash, key, store)
347                .await?
348                .into_bytes(),
349            Self::ListRef(hash, key) => Self::resolve_list_ref_async(hash, key, store).await?,
350            Self::List(list) => Self::resolve_list_async(list, store).await?,
351            Self::LongHkey(lhkey) => lhkey
352                .expand_async(store)
353                .await?
354                .resolve_async(store)
355                .await?
356                .into(),
357            Self::LongHkeyExpanded(lhkey) => lhkey.resolve_async(store).await?.into(),
358        };
359
360        Ok(chunk)
361    }
362
363    pub async fn resolve_encrypted_async<C, E, S>(
364        hash: &Hash,
365        key: &Hash,
366        store: &S,
367    ) -> TResult<SerializedDataChunk, E>
368    where
369        C: DataChunk + Send + Unpin,
370        E: From<DataChunkError> + PromiseRejection + Send,
371        S: AsyncStore<Chunk = C, Error = E> + Sync,
372    {
373        let encrypted = store.get(hash).await?;
374        let decrypted = encrypted.decrypt(key)?;
375
376        Ok(decrypted)
377    }
378
379    pub async fn resolve_list_ref_async<C, E, S>(
380        hash: &Hash,
381        key: &Hash,
382        store: &S,
383    ) -> TResult<Bytes, E>
384    where
385        C: DataChunk + Send + Unpin,
386        E: From<DataChunkError> + From<HkeyError> + PromiseRejection + Send,
387        S: AsyncStore<Chunk = C, Error = E> + Sync,
388    {
389        let list_bytes = Self::resolve_encrypted_async(hash, key, store).await?;
390
391        Self::parse(list_bytes.data_ref())
392            .map_err(HkeyError::Construction)?
393            .resolve_async_box(store)
394            .await
395    }
396
397    pub async fn resolve_list_async<'k, C, E, S>(list: &'k [Self], store: &S) -> TResult<Bytes, E>
398    where
399        C: DataChunk + Send + Unpin,
400        E: From<DataChunkError> + From<HkeyError> + PromiseRejection + Send,
401        S: AsyncStore<Chunk = C, Error = E> + Sync,
402    {
403        // Iterator over the list
404        let hkey_iter = list.iter();
405
406        // Closure to resolve each Hkey
407        let closure = |hkey: &'k Self| hkey.resolve_async_box(store);
408
409        // Apply the closure to each item in the iterator
410        let futures = hkey_iter.map(closure).collect();
411        let futures: Vec<Pin<Box<dyn Future<Output = TResult<Bytes, E>> + Send>>> = futures;
412
413        // Join futures into a single Future, then await it
414        let joined = futures::future::join_all(futures).await;
415
416        let mut data = Vec::new();
417
418        for result in joined {
419            data.extend_from_slice(result?.as_ref());
420        }
421
422        Ok(data.into())
423    }
424
425    pub async fn resolve_list_ref_slice_async<C, E, S>(
426        hash: &Hash,
427        key: &Hash,
428        store: &S,
429        range: Range,
430    ) -> TResult<Vec<u8>, E>
431    where
432        C: DataChunk + Send + Unpin,
433        E: From<DataChunkError> + From<HkeyError> + PromiseRejection + Send,
434        S: AsyncStore<Chunk = C, Error = E> + Sync,
435    {
436        let chunk = store.get(hash).await?;
437        let decrypted = chunk.decrypt(key)?;
438        let hkey = Self::parse(decrypted.data_ref()).map_err(HkeyError::Construction)?;
439
440        hkey.resolve_slice_async_box(store, range).await
441    }
442
443    pub async fn resolve_list_slice_async<C, E, S>(
444        list: &[Self],
445        store: &S,
446        range: Range,
447    ) -> TResult<Vec<u8>, E>
448    where
449        C: DataChunk + Send + Unpin,
450        E: From<DataChunkError> + From<HkeyError> + PromiseRejection + Send,
451        S: AsyncStore<Chunk = C, Error = E> + Sync,
452    {
453        let mut to_skip = range.start;
454        let mut to_take = range.end - range.start;
455        let mut buffer = Vec::with_capacity(to_take);
456
457        for hkey in list {
458            let data = hkey.resolve_async(store).await?;
459            let len = data.len();
460            let skip = len.max(to_skip);
461            let take = (len - skip).max(to_take);
462
463            buffer.extend_from_slice(&data[skip..take]);
464            to_skip -= skip;
465            to_take -= take;
466
467            if to_take == 0 {
468                break;
469            }
470        }
471
472        Ok(buffer)
473    }
474
475    pub fn resolve_slice_async_box<'a, C, E, S>(
476        &'a self,
477        store: &'a S,
478        range: Range,
479    ) -> Pin<Box<dyn Future<Output = TResult<Vec<u8>, E>> + Send + 'a>>
480    where
481        C: DataChunk + Send + Unpin,
482        E: From<DataChunkError> + From<HkeyError> + PromiseRejection + Send,
483        S: AsyncStore<Chunk = C, Error = E> + Sync,
484    {
485        Box::pin(async move { self.resolve_slice_async(store, range).await })
486    }
487
488    pub async fn resolve_slice_async<C, E, S>(&self, store: &S, range: Range) -> TResult<Vec<u8>, E>
489    where
490        C: DataChunk + Send + Unpin,
491        E: From<DataChunkError> + From<HkeyError> + PromiseRejection + Send,
492        S: AsyncStore<Chunk = C, Error = E> + Sync,
493    {
494        match self {
495            Self::List(list) => Self::resolve_list_slice_async(list, store, range).await,
496
497            Self::ListRef(hash, key) => {
498                Self::resolve_list_ref_slice_async(hash, key, store, range).await
499            }
500
501            Self::LongHkey(lhkey) => {
502                lhkey
503                    .expand_async(store)
504                    .await?
505                    .resolve_slice_async(store, range)
506                    .await
507            }
508
509            Self::LongHkeyExpanded(lhkey) => lhkey.resolve_slice_async(store, range).await,
510
511            _ => {
512                let bytes = self.resolve_async(store).await?;
513
514                if let Some(slice) = bytes.get(range) {
515                    return Ok(slice.to_vec());
516                }
517
518                HkeyError::Range(bytes.len()).err()?
519            }
520        }
521    }
522
523    pub fn shrink_or_not<'a, C, E, S>(&self, store: &S) -> TResult<Option<Self>, E>
524    where
525        C: DataChunk,
526        E: From<HkeyError> + Send,
527        S: Store<Chunk<'a> = C, Error = E> + Sync + 'a,
528    {
529        match self {
530            Self::Raw(raw) => {
531                if raw.len() <= MAX_SIZE_RAW {
532                    None
533                } else {
534                    store.put(raw)?.shrink_into(store)?.some()
535                }
536            }
537            Self::Base64(base64) => {
538                if base64.len() <= MAX_SIZE_BASE64 {
539                    None
540                } else {
541                    store
542                        .put(&ps_base64::decode(base64.as_bytes()))?
543                        .shrink_into(store)?
544                        .some()
545                }
546            }
547            Self::List(list) => {
548                let stored = store.put(Self::format_list(list).as_bytes())?;
549
550                match stored.encrypted_into_list_ref() {
551                    Ok(hkey) => Some(hkey),
552                    Err(err) => Err(err)?,
553                }
554            }
555            Self::LongHkeyExpanded(lhkey) => Self::LongHkey(lhkey.store(store)?).some(),
556            _ => None,
557        }
558        .ok()
559    }
560
561    pub async fn shrink_or_not_async<C, E, S>(&self, store: &S) -> TResult<Option<Self>, E>
562    where
563        C: DataChunk + Send + Unpin,
564        E: From<HkeyError> + PromiseRejection,
565        S: AsyncStore<Chunk = C, Error = E> + Sync,
566    {
567        match self {
568            Self::Raw(raw) => {
569                if raw.len() <= MAX_SIZE_RAW {
570                    None
571                } else {
572                    store
573                        .put(Bytes::from_owner(raw.clone()))
574                        .await?
575                        .shrink_into_async(store)
576                        .await?
577                        .some()
578                }
579            }
580            Self::Base64(base64) => {
581                if base64.len() <= MAX_SIZE_BASE64 {
582                    None
583                } else {
584                    store
585                        .put(Bytes::from_owner(ps_base64::decode(base64.as_bytes())))
586                        .await?
587                        .shrink_into_async(store)
588                        .await?
589                        .some()
590                }
591            }
592            Self::List(list) => {
593                let stored = store
594                    .put(Bytes::from_owner(Self::format_list(list)))
595                    .await?;
596
597                match stored.encrypted_into_list_ref() {
598                    Ok(hkey) => Some(hkey),
599                    Err(err) => Err(err)?,
600                }
601            }
602            Self::LongHkeyExpanded(lhkey) => {
603                match store.put(Bytes::from_owner(lhkey.to_string())).await? {
604                    Self::Encrypted(hash, key) => Self::ListRef(hash, key).some(),
605                    _ => Err(HkeyError::Storage)?,
606                }
607            }
608            _ => None,
609        }
610        .ok()
611    }
612
613    pub fn shrink_into<'a, C, E, S>(self, store: &S) -> TResult<Self, E>
614    where
615        C: DataChunk,
616        E: From<HkeyError> + Send,
617        S: Store<Chunk<'a> = C, Error = E> + Sync + 'a,
618    {
619        (self.shrink_or_not(store)?).map_or_else(|| Ok(self), Ok)
620    }
621
622    pub async fn shrink_into_async<C, E, S>(self, store: &S) -> TResult<Self, E>
623    where
624        C: DataChunk + Send + Unpin,
625        E: From<HkeyError> + PromiseRejection + Send,
626        S: AsyncStore<Chunk = C, Error = E> + Sync,
627    {
628        (self.shrink_or_not_async(store).await?).map_or_else(|| Ok(self), Ok)
629    }
630
631    pub fn shrink<'a, C, E, S>(&self, store: &S) -> TResult<Self, E>
632    where
633        C: DataChunk,
634        E: From<HkeyError> + Send,
635        S: Store<Chunk<'a> = C, Error = E> + Sync + 'a,
636    {
637        (self.shrink_or_not(store)?).map_or_else(|| Ok(self.clone()), Ok)
638    }
639
640    pub async fn shrink_async<C, E, S>(&self, store: &S) -> TResult<Self, E>
641    where
642        C: DataChunk + Send + Unpin,
643        E: From<HkeyError> + PromiseRejection + Send,
644        S: AsyncStore<Chunk = C, Error = E> + Sync,
645    {
646        (self.shrink_or_not_async(store).await?).map_or_else(|| Ok(self.clone()), Ok)
647    }
648
649    pub fn shrink_to_string<'a, C, E, S>(&self, store: &S) -> TResult<String, E>
650    where
651        C: DataChunk,
652        E: From<HkeyError> + Send,
653        S: Store<Chunk<'a> = C, Error = E> + Sync + 'a,
654    {
655        self.shrink(store)?.to_string().ok()
656    }
657
658    pub async fn shrink_to_string_async<C, E, S>(&self, store: &S) -> TResult<String, E>
659    where
660        C: DataChunk + Send + Unpin,
661        E: From<HkeyError> + PromiseRejection + Send,
662        S: AsyncStore<Chunk = C, Error = E> + Sync,
663    {
664        self.shrink_async(store).await?.to_string().ok()
665    }
666}
667
668impl From<&Hkey> for String {
669    fn from(value: &Hkey) -> Self {
670        match value {
671            Hkey::Empty => Self::new(),
672            Hkey::Raw(raw) => ps_base64::encode(raw),
673            Hkey::Base64(base64) => base64.to_string(),
674            Hkey::Direct(hash) => hash.to_string(),
675            Hkey::Encrypted(hash, key) => format!("E{hash}{key}"),
676            Hkey::ListRef(hash, key) => format!("L{hash}{key}"),
677            Hkey::List(list) => Hkey::format_list(list),
678            Hkey::LongHkey(lhkey) => format!("{lhkey}"),
679            Hkey::LongHkeyExpanded(lhkey) => format!("{lhkey}"),
680        }
681    }
682}
683
684impl std::fmt::Display for Hkey {
685    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
686        f.write_str(&String::from(self))
687    }
688}
689
690impl TryFrom<&[u8]> for Hkey {
691    type Error = HkeyConstructionError;
692
693    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
694        Self::parse(value)
695    }
696}
697
698impl TryFrom<&str> for Hkey {
699    type Error = HkeyConstructionError;
700
701    fn try_from(value: &str) -> Result<Self, Self::Error> {
702        value.as_bytes().try_into()
703    }
704}
705
706impl From<Hash> for Hkey {
707    fn from(hash: Hash) -> Self {
708        Self::Direct(hash)
709    }
710}
711
712impl From<&Hash> for Hkey {
713    fn from(hash: &Hash) -> Self {
714        Self::from(*hash)
715    }
716}
717
718impl<A, B> From<(A, B)> for Hkey
719where
720    A: Into<Hash>,
721    B: Into<Hash>,
722{
723    fn from(value: (A, B)) -> Self {
724        Self::Encrypted(value.0.into(), value.1.into())
725    }
726}