1use std::{collections::HashSet, num::ParseIntError, str::FromStr};
5
6use futures::future;
7use linera_base::{
8 crypto::CryptoError,
9 data_types::{TimeDelta, Timestamp},
10 identifiers::{ApplicationId, ChainId, GenericApplicationId},
11 time::Duration,
12};
13use linera_core::{data_types::RoundTimeout, node::NotificationStream, worker::Reason};
14use tokio_stream::StreamExt as _;
15
16pub fn parse_json<T: serde::de::DeserializeOwned>(s: &str) -> anyhow::Result<T> {
18 Ok(serde_json::from_str(s.trim())?)
19}
20
21pub fn parse_millis(s: &str) -> Result<Duration, ParseIntError> {
23 Ok(Duration::from_millis(s.parse()?))
24}
25
26pub fn non_zero_duration(d: Duration) -> Option<Duration> {
28 if d.is_zero() {
29 None
30 } else {
31 Some(d)
32 }
33}
34
35pub fn parse_secs(s: &str) -> Result<Duration, ParseIntError> {
37 Ok(Duration::from_secs(s.parse()?))
38}
39
40pub fn parse_millis_delta(s: &str) -> Result<TimeDelta, ParseIntError> {
42 Ok(TimeDelta::from_millis(s.parse()?))
43}
44
45pub fn parse_json_optional_millis_delta(s: &str) -> anyhow::Result<Option<TimeDelta>> {
47 Ok(parse_json::<Option<u64>>(s)?.map(TimeDelta::from_millis))
48}
49
50pub fn parse_chain_set(s: &str) -> Result<HashSet<ChainId>, CryptoError> {
52 match s.trim() {
53 "" => Ok(HashSet::new()),
54 s => s.split(",").map(ChainId::from_str).collect(),
55 }
56}
57
58pub fn parse_app_set(s: &str) -> anyhow::Result<HashSet<GenericApplicationId>> {
60 s.trim()
61 .split(",")
62 .map(|app_str| {
63 GenericApplicationId::from_str(app_str)
64 .or_else(|_| Ok(ApplicationId::from_str(app_str)?.into()))
65 })
66 .collect()
67}
68
69pub async fn wait_for_next_round(stream: &mut NotificationStream, timeout: RoundTimeout) {
71 let mut stream = stream.filter(|notification| match ¬ification.reason {
72 Reason::NewBlock { height, .. } | Reason::NewEvents { height, .. } => {
73 *height >= timeout.next_block_height
74 }
75 Reason::NewRound { round, .. } => *round > timeout.current_round,
76 Reason::NewIncomingBundle { .. } | Reason::BlockExecuted { .. } => false,
77 });
78 future::select(
79 Box::pin(stream.next()),
80 Box::pin(linera_base::time::timer::sleep(
81 timeout.timestamp.duration_since(Timestamp::now()),
82 )),
83 )
84 .await;
85}
86
87macro_rules! impl_from_infallible {
88 ($target:path) => {
89 impl From<::std::convert::Infallible> for $target {
90 fn from(infallible: ::std::convert::Infallible) -> Self {
91 match infallible {}
92 }
93 }
94 };
95}
96
97pub(crate) use impl_from_infallible;