1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// Copyright (c) Zefchain Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use std::{collections::HashSet, num::ParseIntError, str::FromStr};

use futures::future;
use linera_base::{
    crypto::CryptoError,
    data_types::{TimeDelta, Timestamp},
    identifiers::ChainId,
    time::Duration,
};
use linera_core::{data_types::RoundTimeout, node::NotificationStream, worker::Reason};
use tokio_stream::StreamExt as _;

pub fn parse_millis(s: &str) -> Result<Duration, ParseIntError> {
    Ok(Duration::from_millis(s.parse()?))
}

pub fn parse_secs(s: &str) -> Result<Duration, ParseIntError> {
    Ok(Duration::from_secs(s.parse()?))
}

pub fn parse_millis_delta(s: &str) -> Result<TimeDelta, ParseIntError> {
    Ok(TimeDelta::from_millis(s.parse()?))
}

pub fn parse_chain_set(s: &str) -> Result<HashSet<ChainId>, CryptoError> {
    match s.trim() {
        "" => Ok(HashSet::new()),
        s => s.split(",").map(ChainId::from_str).collect(),
    }
}

pub fn parse_ascii_alphanumeric_string(s: &str) -> Result<String, &'static str> {
    if s.chars().all(|x| x.is_ascii_alphanumeric()) {
        Ok(s.to_string())
    } else {
        Err("Expecting ASCII alphanumeric characters")
    }
}

/// Returns after the specified time or if we receive a notification that a new round has started.
pub async fn wait_for_next_round(stream: &mut NotificationStream, timeout: RoundTimeout) {
    let mut stream = stream.filter(|notification| match &notification.reason {
        Reason::NewBlock { height, .. } => *height >= timeout.next_block_height,
        Reason::NewRound { round, .. } => *round > timeout.current_round,
        Reason::NewIncomingBundle { .. } => false,
    });
    future::select(
        Box::pin(stream.next()),
        Box::pin(linera_base::time::timer::sleep(
            timeout.timestamp.duration_since(Timestamp::now()),
        )),
    )
    .await;
}

macro_rules! impl_from_dynamic {
    ($target:ty : $variant:ident, $source:ty) => {
        impl From<$source> for $target {
            fn from(error: $source) -> Self {
                <$target>::$variant(Box::new(error))
            }
        }
    };
}

macro_rules! impl_from_infallible {
    ($target:path) => {
        impl From<::std::convert::Infallible> for $target {
            fn from(infallible: ::std::convert::Infallible) -> Self {
                match infallible {}
            }
        }
    };
}

pub(crate) use impl_from_dynamic;
pub(crate) use impl_from_infallible;