Skip to main content

tmprl_client/ops/
namespace.rs

1//! Namespace listing.
2
3use temporalio_client::tonic::Request;
4use temporalio_common::protos::temporal::api::workflowservice::v1::ListNamespacesRequest;
5
6use super::OpError;
7use crate::Conn;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct NamespaceInfo {
11    pub name: String,
12    pub state: String,
13    /// Retention in whole days. Temporal stores this as a duration; days is what the UI shows.
14    pub retention_days: i64,
15    pub description: String,
16}
17
18impl Conn {
19    /// Every namespace on the cluster, paged to exhaustion.
20    ///
21    /// Namespace counts are small (tens, not thousands), so this collects rather than
22    /// streaming. Workflow listing will not be able to do that.
23    pub async fn list_namespaces(&self) -> Result<Vec<NamespaceInfo>, OpError> {
24        let mut wf = self.wf();
25        let mut out = Vec::new();
26        let mut page_token = Vec::new();
27
28        loop {
29            let resp = wf
30                .list_namespaces(Request::new(ListNamespacesRequest {
31                    page_size: 100,
32                    next_page_token: page_token,
33                    ..Default::default()
34                }))
35                .await
36                .map_err(|s| OpError::rpc("ListNamespaces", s))?
37                .into_inner();
38
39            for ns in resp.namespaces {
40                let Some(info) = ns.namespace_info else {
41                    continue;
42                };
43                // `state()` borrows, so read it before moving the string fields out.
44                let state = format!("{:?}", info.state());
45                out.push(NamespaceInfo {
46                    name: info.name,
47                    state,
48                    retention_days: ns
49                        .config
50                        .and_then(|c| c.workflow_execution_retention_ttl)
51                        .map(|d| d.seconds / 86_400)
52                        .unwrap_or(0),
53                    description: info.description,
54                });
55            }
56
57            if resp.next_page_token.is_empty() {
58                break;
59            }
60            page_token = resp.next_page_token;
61        }
62
63        out.sort_by(|a, b| a.name.cmp(&b.name));
64        Ok(out)
65    }
66}