wasmer_cli/commands/package/common/
wait.rs

1use futures::StreamExt;
2use wasmer_backend_api::{WasmerClient, types::PackageVersionState};
3
4/// Different conditions that can be "awaited" when publishing a package.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
6pub enum PublishWait {
7    None,
8    Container,
9    NativeExecutables,
10    Bindings,
11    All,
12}
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct WaitPackageState {
16    pub container: bool,
17    pub native_executables: bool,
18    pub bindings: bool,
19}
20
21impl WaitPackageState {
22    pub fn is_any(self) -> bool {
23        self.container || self.native_executables || self.bindings
24    }
25
26    pub fn new_none() -> Self {
27        Self {
28            container: false,
29            native_executables: false,
30            bindings: false,
31        }
32    }
33
34    pub fn new_all() -> Self {
35        Self {
36            container: true,
37            native_executables: true,
38            bindings: true,
39        }
40    }
41
42    pub fn new_container() -> Self {
43        Self {
44            container: true,
45            native_executables: false,
46            bindings: false,
47        }
48    }
49
50    pub fn new_exe() -> Self {
51        Self {
52            container: true,
53            native_executables: true,
54            bindings: false,
55        }
56    }
57
58    pub fn new_bindings() -> Self {
59        Self {
60            container: true,
61            native_executables: false,
62            bindings: true,
63        }
64    }
65}
66
67impl From<PublishWait> for WaitPackageState {
68    fn from(value: PublishWait) -> Self {
69        match value {
70            PublishWait::None => Self::new_none(),
71            PublishWait::Container => Self::new_container(),
72            PublishWait::NativeExecutables => Self::new_exe(),
73            PublishWait::Bindings => Self::new_bindings(),
74            PublishWait::All => Self::new_all(),
75        }
76    }
77}
78
79pub async fn wait_package(
80    client: &WasmerClient,
81    to_wait: PublishWait,
82    package_version_id: wasmer_backend_api::types::Id,
83    timeout: humantime::Duration,
84) -> anyhow::Result<()> {
85    if let PublishWait::None = to_wait {
86        return Ok(());
87    }
88
89    let package_version_id = package_version_id.into_inner();
90
91    let mut stream =
92        wasmer_backend_api::subscription::package_version_ready(client, &package_version_id)
93            .await?;
94
95    let mut state: WaitPackageState = to_wait.into();
96
97    let deadline: std::time::Instant =
98        std::time::Instant::now() + std::time::Duration::from_secs(timeout.as_secs());
99
100    loop {
101        if !state.is_any() {
102            break;
103        }
104
105        if std::time::Instant::now() > deadline {
106            return Err(anyhow::anyhow!(
107                "Timed out waiting for package version to become ready"
108            ));
109        }
110
111        let data = match tokio::time::timeout_at(deadline.into(), stream.next()).await {
112            Err(_) => {
113                return Err(anyhow::anyhow!(
114                    "Timed out waiting for package version to become ready"
115                ));
116            }
117            Ok(None) => {
118                break;
119            }
120            Ok(Some(data)) => data,
121        };
122
123        if let Some(data) = data.unwrap().data {
124            match data.package_version_ready.state {
125                PackageVersionState::WebcGenerated => state.container = false,
126                PackageVersionState::BindingsGenerated => state.bindings = false,
127                PackageVersionState::NativeExesGenerated => state.native_executables = false,
128            }
129        }
130    }
131
132    Ok(())
133}