Skip to main content

akribes_sdk/sub/
versions.rs

1use std::sync::Arc;
2
3use crate::client::{AkribesClient, Inner};
4use crate::error::Result;
5use crate::models::*;
6
7/// Sub-client for script versions. Obtained via `AkribesClient::project(id).versions()`.
8#[derive(Clone, Debug)]
9pub struct VersionsClient {
10    pub(crate) inner: Arc<Inner>,
11    pub(crate) project_id: i64,
12}
13
14impl VersionsClient {
15    pub(crate) fn new(inner: Arc<Inner>, project_id: i64) -> Self {
16        Self { inner, project_id }
17    }
18
19    fn c(&self) -> AkribesClient {
20        AkribesClient {
21            inner: Arc::clone(&self.inner),
22        }
23    }
24
25    fn script_url(&self, name: &str) -> String {
26        format!(
27            "{}/projects/{}/scripts/{}",
28            self.inner.base_url,
29            self.project_id,
30            urlencoding::encode(name)
31        )
32    }
33
34    pub async fn list(&self, script_name: &str) -> Result<Vec<ScriptVersion>> {
35        let url = format!("{}/versions", self.script_url(script_name));
36        self.c().get_list(&url).await
37    }
38
39    pub async fn get(&self, script_name: &str, version_id: i64) -> Result<Option<ScriptVersion>> {
40        let url = format!("{}/versions/{}", self.script_url(script_name), version_id);
41        self.c().get_opt(&url).await
42    }
43
44    pub async fn get_latest(&self, script_name: &str) -> Result<Option<LatestVersion>> {
45        let url = format!("{}/latest", self.script_url(script_name));
46        self.c().get_opt(&url).await
47    }
48
49    /// Start building a publish operation.
50    pub fn publish(&self, script_name: &str) -> PublishBuilder {
51        PublishBuilder {
52            client: self.c(),
53            project_id: self.project_id,
54            script_name: script_name.to_string(),
55            channels: vec![],
56            label: None,
57            published_by: None,
58            force: None,
59            dry_run: None,
60        }
61    }
62
63    /// Publish directly without a builder.
64    pub async fn publish_version(
65        &self,
66        script_name: &str,
67        channels: Vec<String>,
68        label: Option<&str>,
69        published_by: Option<&str>,
70    ) -> Result<ScriptVersion> {
71        let url = format!("{}/publish", self.script_url(script_name));
72        let resp: PublishResponse = self
73            .c()
74            .post(
75                &url,
76                &PublishRequest {
77                    channels,
78                    label: label.map(|s| s.to_string()),
79                    published_by: published_by.map(|s| s.to_string()),
80                    force: None,
81                    dry_run: None,
82                },
83            )
84            .await?;
85        Ok(resp.version)
86    }
87}
88
89/// Builder for publishing a script version.
90#[derive(Debug, Clone)]
91#[must_use = "a builder does nothing until .execute() is called"]
92pub struct PublishBuilder {
93    client: AkribesClient,
94    project_id: i64,
95    script_name: String,
96    channels: Vec<String>,
97    label: Option<String>,
98    published_by: Option<String>,
99    force: Option<bool>,
100    dry_run: Option<bool>,
101}
102
103impl PublishBuilder {
104    /// Set the channels to publish to.
105    pub fn channels(mut self, channels: Vec<String>) -> Self {
106        self.channels = channels;
107        self
108    }
109
110    /// Set a label for this version.
111    pub fn label(mut self, label: impl Into<String>) -> Self {
112        self.label = Some(label.into());
113        self
114    }
115
116    /// Set who published this version.
117    pub fn published_by(mut self, published_by: impl Into<String>) -> Self {
118        self.published_by = Some(published_by.into());
119        self
120    }
121
122    /// Force publish even if it would break existing contracts.
123    pub fn force(mut self, force: bool) -> Self {
124        self.force = Some(force);
125        self
126    }
127
128    /// Perform a dry run — check what would break without actually publishing.
129    pub fn dry_run(mut self, dry_run: bool) -> Self {
130        self.dry_run = Some(dry_run);
131        self
132    }
133
134    fn publish_url(&self) -> String {
135        format!(
136            "{}/projects/{}/scripts/{}/publish",
137            self.client.inner.base_url,
138            self.project_id,
139            urlencoding::encode(&self.script_name)
140        )
141    }
142
143    /// Execute the publish and return the new version plus an optional
144    /// rebase summary (present only on first publish — see
145    /// [`PublishOutcome`]). Callers that only need the version can use
146    /// the `.version` field or [`Self::execute_version_only`].
147    pub async fn execute(self) -> Result<crate::models::PublishOutcome> {
148        let url = self.publish_url();
149        let resp: PublishResponse = self
150            .client
151            .post(
152                &url,
153                &PublishRequest {
154                    channels: self.channels,
155                    label: self.label,
156                    published_by: self.published_by,
157                    force: self.force,
158                    dry_run: None,
159                },
160            )
161            .await?;
162        Ok(crate::models::PublishOutcome {
163            version: resp.version,
164            rebased: resp.rebased,
165        })
166    }
167
168    /// Backwards-compat: returns only the new [`ScriptVersion`] without
169    /// the rebase summary. Equivalent to `execute().await?.version` —
170    /// kept so existing callers don't have to thread `PublishOutcome`.
171    pub async fn execute_version_only(self) -> Result<ScriptVersion> {
172        Ok(self.execute().await?.version)
173    }
174
175    /// Execute a dry-run publish — check what would break without publishing.
176    pub async fn execute_dry_run(mut self) -> Result<DryRunResult> {
177        self.dry_run = Some(true);
178        let url = self.publish_url();
179        self.client
180            .post(
181                &url,
182                &PublishRequest {
183                    channels: self.channels,
184                    label: self.label,
185                    published_by: self.published_by,
186                    force: self.force,
187                    dry_run: Some(true),
188                },
189            )
190            .await
191    }
192}