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 #[default]
51 Empty,
52 Raw(ArrayVec<u8, BUF_SIZE_RAW>),
54 Base64(ArrayString<BUF_SIZE_BASE64>),
56 Direct(Hash),
58 Encrypted(Hash, Hash),
60 ListRef(Hash, Hash),
62 List(Arc<[Self]>),
64 LongHkey(LongHkey),
66 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 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 let hkey_iter = list.into_par_iter();
230
231 let closure = |hkey: &Self| hkey.resolve(store);
233
234 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 let hkey_iter = list.iter();
405
406 let closure = |hkey: &'k Self| hkey.resolve_async_box(store);
408
409 let futures = hkey_iter.map(closure).collect();
411 let futures: Vec<Pin<Box<dyn Future<Output = TResult<Bytes, E>> + Send>>> = futures;
412
413 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}