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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! Deployable trait and Deploying enum for trait-based deployments.
//!
//! WHY: Users define infrastructure as Rust code, not YAML or TOML configs.
//! The `Deployable` trait provides a unified interface for deploying and
//! destroying resources across all providers.
//!
//! WHAT: A trait with associated types for output, error, state store, and DNS
//! resolver. `deploy()` and `destroy()` are **async**: they return a
//! [`BoxFuture`] (a `Pin<Box<dyn Future<Output = Result<..>> + Send>>`).
//! Implementors write ordinary async logic and `Box::pin(async move { … })`
//! it — no `async_trait` macro, and the trait stays object-safe.
//!
//! HOW: Users implement `Deployable` on their structs. The methods receive a
//! [`ProviderClient<Store, Resolver>`] for state persistence and (for
//! HTTP-first providers) an API client. A provider whose transport does
//! not fit `ProviderClient`'s HTTP client — e.g. Docker over a Unix socket
//! — may build its own client inside the future and use `ProviderClient`
//! only for state. See `foundation_deployment_docker`'s `ContainerDeployment`.
use Future;
use Pin;
use DnsResolver;
use NamespacedStore;
use StateStore;
use crateProviderClient;
/// A boxed, `Send` future — the return type of [`Deployable::deploy`] and
/// [`Deployable::destroy`].
///
/// Boxing keeps the trait object-safe and lets implementors write plain
/// `Box::pin(async move { … })` without `async_trait` or unstable
/// return-position-`impl`-Trait-in-trait Send plumbing. Any valtron task can
/// also be adapted into one of these futures, so this is the single async
/// interface deployment code speaks.
pub type BoxFuture<'a, T> = ;
/// Generic progress states for deployment and destroy execution.
///
/// WHY: All deployments share the same progress states - no need for custom enums.
///
/// WHAT: Simple enum with `Init`, `Processing`, `Done`, and `Failed` variants.
///
/// HOW: A general-purpose progress signal a caller may report while awaiting a
/// [`Deployable`] future (the trait itself is plain async and does not require
/// it).
/// Trait for deployable and destroyable infrastructure.
///
/// WHY: Users define infrastructure as Rust types implementing this trait.
/// No YAML, TOML, or custom configuration formats needed.
///
/// WHAT: Trait with associated types for deploy output, destroy output, error,
/// state store, and DNS resolver. `deploy()` and `destroy()` are async —
/// they return a [`BoxFuture`] the caller `.await`s.
///
/// HOW: Implement on user structs. Methods receive `ProviderClient<Store, Resolver>`
/// providing state persistence and HTTP client access. Write the real work
/// as an `async move` block and `Box::pin` it.
///
/// # Associated Types
///
/// * `DeployOutput` - Deployment output type containing URLs, IDs, and artifacts
/// * `DestroyOutput` - Destruction output type (often `()` but can contain metadata)
/// * `Error` - Error type implementing `std::error::Error + Send + Sync + Debug`
/// * `Store` - State store implementation for persistence
/// * `Resolver` - DNS resolver for HTTP calls (e.g., `SystemDnsResolver` or `StaticSocketAddr`)
///
/// # Examples
///
/// ```rust,no_run
/// use foundation_deployment::traits::{BoxFuture, Deployable};
/// use foundation_deployment::provider_client::ProviderClient;
/// use foundation_db::core::state::FileStateStore;
/// use foundation_netio::shared::client::dns::SystemDnsResolver;
///
/// struct MyWorker {
/// name: String,
/// }
///
/// impl Deployable for MyWorker {
/// const NAMESPACE: &'static str = "cloudflare/workers/script";
///
/// type DeployOutput = String;
/// type DestroyOutput = ();
/// type Error = std::io::Error;
/// type Store = FileStateStore;
/// type Resolver = SystemDnsResolver;
///
/// fn deploy(
/// &self,
/// instance_id: usize,
/// client: ProviderClient<Self::Store, Self::Resolver>,
/// ) -> BoxFuture<'static, Result<Self::DeployOutput, Self::Error>> {
/// let name = self.name.clone();
/// Box::pin(async move {
/// // ... real async deploy logic ...
/// Ok(name)
/// })
/// }
///
/// fn destroy(
/// &self,
/// instance_id: usize,
/// client: ProviderClient<Self::Store, Self::Resolver>,
/// ) -> BoxFuture<'static, Result<Self::DestroyOutput, Self::Error>> {
/// Box::pin(async move { Ok(()) })
/// }
/// }
/// ```