Skip to main content

alien_bindings/
traits.rs

1use crate::error::Result;
2use crate::presigned::PresignedRequest;
3use alien_core::{BuildConfig, BuildExecution};
4use async_trait::async_trait;
5use object_store::path::Path;
6use object_store::ObjectStore;
7use serde::{Deserialize, Serialize};
8use std::collections::BTreeMap;
9use std::sync::Arc;
10use std::time::Duration;
11use url::Url;
12
13#[cfg(feature = "openapi")]
14use utoipa::ToSchema;
15
16/// Marker trait for all binding types.
17pub trait Binding: Send + Sync + std::fmt::Debug {}
18
19/// A storage binding that provides object store capabilities.
20#[async_trait]
21pub trait Storage: Binding + ObjectStore {
22    /// Gets the base directory path configured for this storage binding.
23    fn get_base_dir(&self) -> Path;
24    /// Gets the underlying URL configured for this storage binding.
25    fn get_url(&self) -> Url;
26
27    /// Creates a presigned request for uploading data to the specified path.
28    /// The request can be serialized, stored, and executed later.
29    async fn presigned_put(&self, path: &Path, expires_in: Duration) -> Result<PresignedRequest>;
30
31    /// Creates a presigned request for downloading data from the specified path.
32    /// The request can be serialized, stored, and executed later.
33    async fn presigned_get(&self, path: &Path, expires_in: Duration) -> Result<PresignedRequest>;
34
35    /// Creates a presigned request for deleting the object at the specified path.
36    /// The request can be serialized, stored, and executed later.
37    async fn presigned_delete(&self, path: &Path, expires_in: Duration)
38        -> Result<PresignedRequest>;
39}
40
41/// A build binding that provides build execution capabilities.
42#[async_trait]
43pub trait Build: Binding {
44    /// Starts a new build with the given configuration.
45    /// Returns the build execution information.
46    async fn start_build(&self, config: BuildConfig) -> Result<BuildExecution>;
47
48    /// Gets the status of a specific build execution.
49    async fn get_build_status(&self, build_id: &str) -> Result<BuildExecution>;
50
51    /// Stops or cancels a running build.
52    async fn stop_build(&self, build_id: &str) -> Result<()>;
53}
54
55/// AWS IAM Role service account information
56#[derive(Debug, Clone, Serialize, Deserialize)]
57#[serde(rename_all = "camelCase")]
58#[cfg_attr(feature = "openapi", derive(ToSchema))]
59pub struct AwsServiceAccountInfo {
60    /// The IAM role name
61    pub role_name: String,
62    /// The IAM role ARN (for AssumeRole)
63    pub role_arn: String,
64}
65
66/// GCP Service Account information
67#[derive(Debug, Clone, Serialize, Deserialize)]
68#[serde(rename_all = "camelCase")]
69#[cfg_attr(feature = "openapi", derive(ToSchema))]
70pub struct GcpServiceAccountInfo {
71    /// The service account email (for impersonation)
72    pub email: String,
73    /// The service account unique ID
74    pub unique_id: String,
75}
76
77/// Azure User-Assigned Managed Identity information
78#[derive(Debug, Clone, Serialize, Deserialize)]
79#[serde(rename_all = "camelCase")]
80#[cfg_attr(feature = "openapi", derive(ToSchema))]
81pub struct AzureServiceAccountInfo {
82    /// The managed identity client ID (for authentication)
83    pub client_id: String,
84    /// The managed identity resource ID (ARM ID)
85    pub resource_id: String,
86    /// The managed identity principal ID
87    pub principal_id: String,
88}
89
90/// Platform-specific service account information
91#[derive(Debug, Clone, Serialize, Deserialize)]
92#[serde(tag = "platform", rename_all = "camelCase")]
93#[cfg_attr(feature = "openapi", derive(ToSchema))]
94pub enum ServiceAccountInfo {
95    /// AWS IAM Role
96    Aws(AwsServiceAccountInfo),
97    /// GCP Service Account
98    Gcp(GcpServiceAccountInfo),
99    /// Azure User-Assigned Managed Identity
100    Azure(AzureServiceAccountInfo),
101}
102
103/// Configuration for impersonation
104#[derive(Debug, Clone)]
105pub struct ImpersonationRequest {
106    /// Optional session name (AWS only)
107    pub session_name: Option<String>,
108    /// Optional session duration in seconds  
109    pub duration_seconds: Option<i32>,
110    /// Optional scopes (GCP only)
111    pub scopes: Option<Vec<String>>,
112}
113
114impl Default for ImpersonationRequest {
115    fn default() -> Self {
116        Self {
117            session_name: None,
118            duration_seconds: Some(3600), // 1 hour default
119            scopes: None,
120        }
121    }
122}
123
124/// A service account binding that provides identity and impersonation capabilities.
125#[async_trait]
126pub trait ServiceAccount: Binding {
127    /// Gets information about the service account
128    async fn get_info(&self) -> Result<ServiceAccountInfo>;
129
130    /// Impersonates the service account and returns credentials as a ClientConfig.
131    ///
132    /// This performs the cloud-specific impersonation:
133    /// - AWS: STS AssumeRole to get temporary credentials
134    /// - GCP: IAM Credentials API generateAccessToken
135    /// - Azure: Uses the attached managed identity (no API call needed)
136    async fn impersonate(&self, request: ImpersonationRequest) -> Result<alien_core::ClientConfig>;
137
138    /// Helper for downcasting trait object
139    fn as_any(&self) -> &dyn std::any::Any;
140}
141
142/// Response from repository operations.
143#[derive(Debug, Clone, Serialize, Deserialize)]
144#[serde(rename_all = "camelCase")]
145#[cfg_attr(feature = "openapi", derive(ToSchema))]
146pub struct RepositoryResponse {
147    /// Repository name.
148    pub name: String,
149    /// Repository URI for pushing/pulling images. None if repository is not ready yet.
150    pub uri: Option<String>,
151    /// Optional creation timestamp in ISO8601 format.
152    pub created_at: Option<String>,
153}
154
155/// Permissions level for artifact registry access.
156#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
157#[serde(rename_all = "kebab-case")]
158#[cfg_attr(feature = "openapi", derive(ToSchema))]
159pub enum ArtifactRegistryPermissions {
160    /// Pull-only access (download artifacts).
161    Pull,
162    /// Push and pull access (upload and download artifacts).
163    PushPull,
164}
165
166/// Credentials for accessing a repository.
167#[derive(Debug, Clone, Serialize, Deserialize)]
168#[serde(rename_all = "camelCase")]
169#[cfg_attr(feature = "openapi", derive(ToSchema))]
170pub struct ArtifactRegistryCredentials {
171    /// Username for authentication.
172    pub username: String,
173    /// Password or token for authentication.
174    pub password: String,
175    /// Optional expiration time in ISO8601 format.
176    pub expires_at: Option<String>,
177}
178
179/// Types of compute services that can access artifact registries.
180#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
181#[serde(rename_all = "kebab-case")]
182#[cfg_attr(feature = "openapi", derive(ToSchema))]
183pub enum ComputeServiceType {
184    /// Serverless functions
185    Function,
186    // In the future, we could add Container, VirtualMachine, Kubernetes, etc.
187}
188
189/// Cross-account access configuration for AWS artifact registries.
190#[derive(Debug, Clone, Serialize, Deserialize)]
191#[serde(rename_all = "camelCase")]
192#[cfg_attr(feature = "openapi", derive(ToSchema))]
193pub struct AwsCrossAccountAccess {
194    /// AWS account IDs that should have cross-account access.
195    pub account_ids: Vec<String>,
196    /// Types of compute services that should have access.
197    pub allowed_service_types: Vec<ComputeServiceType>,
198    /// Specific IAM role ARNs to grant access to.
199    /// These are typically deployment/management roles or service-specific roles.
200    pub role_arns: Vec<String>,
201}
202
203/// Cross-account access configuration for GCP artifact registries.
204#[derive(Debug, Clone, Serialize, Deserialize)]
205#[serde(rename_all = "camelCase")]
206#[cfg_attr(feature = "openapi", derive(ToSchema))]
207pub struct GcpCrossAccountAccess {
208    /// GCP project numbers that should have access.
209    pub project_numbers: Vec<String>,
210    /// Types of compute services that should have access.
211    pub allowed_service_types: Vec<ComputeServiceType>,
212    /// Additional service account emails to grant access to.
213    /// These are typically deployment/management service accounts.
214    pub service_account_emails: Vec<String>,
215}
216
217/// Platform-specific cross-account access configuration.
218#[derive(Debug, Clone, Serialize, Deserialize)]
219#[serde(tag = "platform", rename_all = "lowercase")]
220#[cfg_attr(feature = "openapi", derive(ToSchema))]
221pub enum CrossAccountAccess {
222    /// AWS-specific cross-account access configuration.
223    Aws(AwsCrossAccountAccess),
224    /// GCP-specific cross-account access configuration.
225    Gcp(GcpCrossAccountAccess),
226}
227
228/// Current cross-account access permissions for a repository.
229#[derive(Debug, Clone, Serialize, Deserialize)]
230#[serde(rename_all = "camelCase")]
231#[cfg_attr(feature = "openapi", derive(ToSchema))]
232pub struct CrossAccountPermissions {
233    /// Platform-specific access configuration currently applied.
234    pub access: CrossAccountAccess,
235    /// Timestamp when permissions were last updated.
236    pub last_updated: Option<String>,
237}
238
239/// A trait for artifact registry bindings that provide container image repository management.
240#[async_trait]
241pub trait ArtifactRegistry: Binding {
242    /// Creates a repository within the artifact registry.
243    /// Returns the repository details. URI will be None if repository is still being created.
244    async fn create_repository(&self, repo_name: &str) -> Result<RepositoryResponse>;
245
246    /// Gets repository details including name, URI, and creation time.
247    async fn get_repository(&self, repo_id: &str) -> Result<RepositoryResponse>;
248
249    /// Adds cross-account access permissions for a repository.
250    /// This adds the specified permissions to any existing cross-account permissions.
251    ///
252    /// For AWS: Grants access to specified account IDs with configurable principals and compute service types.
253    /// For GCP: Grants access to serverless robots and service accounts based on compute service types.
254    /// For Azure: Not supported - returns OperationNotSupported error.
255    async fn add_cross_account_access(
256        &self,
257        repo_id: &str,
258        access: CrossAccountAccess,
259    ) -> Result<()>;
260
261    /// Removes cross-account access permissions for a repository.
262    /// This removes the specified permissions from existing cross-account permissions.
263    ///
264    /// For AWS: Removes access for specified account IDs and compute service types.
265    /// For GCP: Removes access for specified project numbers and service accounts.
266    /// For Azure: Not supported - returns OperationNotSupported error.
267    async fn remove_cross_account_access(
268        &self,
269        repo_id: &str,
270        access: CrossAccountAccess,
271    ) -> Result<()>;
272
273    /// Gets the current cross-account access permissions for a repository.
274    /// For Azure: Not supported - returns OperationNotSupported error.
275    async fn get_cross_account_access(&self, repo_id: &str) -> Result<CrossAccountPermissions>;
276
277    /// Generates credentials for accessing a repository with specified permissions.
278    /// On AWS: assumes the relevant role and calls get_authorization_token.
279    /// On GCP: impersonates the relevant service account and gets an oauth token.
280    /// On Azure: uses the built-in token mechanism.
281    async fn generate_credentials(
282        &self,
283        repo_id: &str,
284        permissions: ArtifactRegistryPermissions,
285        ttl_seconds: Option<u32>,
286    ) -> Result<ArtifactRegistryCredentials>;
287
288    /// Deletes a repository and all contained images.
289    async fn delete_repository(&self, repo_id: &str) -> Result<()>;
290}
291
292/// A trait for vault bindings that provide secure secret management.
293#[async_trait]
294pub trait Vault: Binding {
295    /// Gets a secret value by name.
296    async fn get_secret(&self, secret_name: &str) -> Result<String>;
297
298    /// Sets a secret value, creating it if it doesn't exist or updating it if it does.
299    async fn set_secret(&self, secret_name: &str, value: &str) -> Result<()>;
300
301    /// Deletes a secret by name.
302    async fn delete_secret(&self, secret_name: &str) -> Result<()>;
303}
304
305/// Represents options for put operations in KV stores.
306#[derive(Debug, Clone, Default)]
307pub struct PutOptions {
308    /// Optional TTL for automatic expiration (soft hint - items MAY be deleted after expiry)
309    pub ttl: Option<Duration>,
310    /// Only put if the key does not exist
311    pub if_not_exists: bool,
312}
313
314/// Represents the result of a scan operation.
315#[derive(Debug)]
316pub struct ScanResult {
317    /// Key-value pairs found (may be ≤ limit, no guarantee to fill)
318    pub items: Vec<(String, Vec<u8>)>,
319    /// Opaque cursor for pagination. None if no more results.
320    /// **Warning**: Cursor may become invalid if data changes. No TTL guarantees.
321    pub next_cursor: Option<String>,
322}
323
324/// A trait for key-value store bindings that provide minimal, platform-agnostic KV operations.
325/// This API is designed to work consistently across DynamoDB, Firestore, Redis, and Azure Table Storage.
326#[async_trait]
327pub trait Kv: Binding {
328    /// Get a value by key. Returns None if key doesn't exist or has expired.
329    ///
330    /// **TTL Behavior**: TTL is a soft hint for automatic cleanup. If `now >= expires_at`,
331    /// implementations SHOULD behave as if the key is absent, even if the item still exists
332    /// physically in the backend. Physical deletion is eventual and not guaranteed.
333    ///
334    /// **Validation**: Keys are validated against MAX_KEY_BYTES and portable charset.
335    /// Invalid keys return `KvError::InvalidKey` immediately.
336    async fn get(&self, key: &str) -> Result<Option<Vec<u8>>>;
337
338    /// Put a value with optional options. When options.if_not_exists is true, returns true if created,
339    /// false if already exists. When options.if_not_exists is false or options is None, always returns true.
340    ///
341    /// **Size Limits**:
342    /// - Keys: ≤ MAX_KEY_BYTES (512 bytes) with portable ASCII charset
343    /// - Values: ≤ MAX_VALUE_BYTES (24,576 bytes = 24 KiB)
344    ///
345    /// **Validation**: Size and charset constraints are enforced before backend calls.
346    /// Invalid inputs return `KvError::InvalidKey` or `KvError::InvalidValue` immediately.
347    ///
348    /// **TTL Behavior**: TTL is a soft hint for automatic cleanup. If TTL is specified,
349    /// item expires at `put_time + ttl`. Expired items SHOULD appear absent on subsequent
350    /// reads, but physical deletion is eventual and not guaranteed.
351    ///
352    /// **Conditional Logic**: The if_not_exists operation maps to backend primitives:
353    /// - Redis: SETNX
354    /// - DynamoDB: PutItem with condition_expression="attribute_not_exists(pk)"
355    /// - Firestore: create() with Precondition::DoesNotExist
356    /// - Azure Table Storage: InsertEntity (409 on conflict)
357    async fn put(&self, key: &str, value: Vec<u8>, options: Option<PutOptions>) -> Result<bool>;
358
359    /// Delete a key. No error if key doesn't exist.
360    ///
361    /// **Validation**: Keys are validated against MAX_KEY_BYTES and portable charset.
362    /// Invalid keys return `KvError::InvalidKey` immediately.
363    async fn delete(&self, key: &str) -> Result<()>;
364
365    /// Check if a key exists without retrieving the value.
366    ///
367    /// **TTL Behavior**: TTL is a soft hint for automatic cleanup. If `now >= expires_at`,
368    /// SHOULD return false even if physically present. Physical deletion is eventual and not guaranteed.
369    ///
370    /// **Validation**: Keys are validated against MAX_KEY_BYTES and portable charset.
371    /// Invalid keys return `KvError::InvalidKey` immediately.
372    async fn exists(&self, key: &str) -> Result<bool>;
373
374    /// Scan keys with a prefix, with pagination support.
375    ///
376    /// **Scan Contract**:
377    /// - Returns an **arbitrary, unordered subset** in backend-natural order
378    /// - **No ordering guarantees** across backends (Redis SCAN, Azure fan-out, etc.)
379    /// - **May return ≤ limit items** (not guaranteed to fill even if more data exists)
380    /// - **Clients MUST de-duplicate** keys across pages (backends may return duplicates)
381    /// - **No completeness guarantee** under concurrent writes (may miss or duplicate)
382    ///
383    /// **Cursor Behavior**:
384    /// - Opaque string, implementation-specific format
385    /// - **May become invalid** anytime after backend state changes
386    /// - **No TTL guarantees** - can expire without notice
387    /// - Passing invalid cursor should return error, not partial results
388    ///
389    /// **TTL Behavior**: TTL is a soft hint for automatic cleanup. Expired items SHOULD
390    /// be filtered out from results, but physical deletion is eventual and not guaranteed.
391    ///
392    /// **Validation**: Prefix follows same key validation rules.
393    /// Invalid prefix returns `KvError::InvalidKey` immediately.
394    async fn scan_prefix(
395        &self,
396        prefix: &str,
397        limit: Option<usize>,
398        cursor: Option<String>,
399    ) -> Result<ScanResult>;
400}
401
402/// JSON/Text message payload for Queue
403#[derive(Debug, Clone, Serialize, Deserialize)]
404#[serde(tag = "type", rename_all = "lowercase")]
405#[cfg_attr(feature = "openapi", derive(ToSchema))]
406pub enum MessagePayload {
407    /// JSON-serializable value
408    Json(serde_json::Value),
409    /// UTF-8 text payload
410    Text(String),
411}
412
413/// A queue message with payload and receipt handle for acknowledgment
414#[derive(Debug, Clone, Serialize, Deserialize)]
415#[serde(rename_all = "camelCase")]
416#[cfg_attr(feature = "openapi", derive(ToSchema))]
417pub struct QueueMessage {
418    /// JSON-first message payload
419    pub payload: MessagePayload,
420    /// Opaque receipt handle for acknowledgment (backend-specific, short-lived)
421    pub receipt_handle: String,
422}
423
424/// Maximum message size in bytes (64 KiB = 65,536 bytes)
425///
426/// This limit ensures compatibility across all queue backends:
427/// - **AWS SQS**: 256KB message limit (much higher, not constraining)
428/// - **Azure Service Bus**: 1MB message limit (much higher, not constraining)  
429/// - **GCP Pub/Sub**: 10MB message limit (much higher, not constraining)
430///
431/// The 64KB limit provides:
432/// - Reasonable message sizes for most use cases
433/// - Fast network transfer and low latency
434/// - Consistent behavior across all cloud providers
435/// - Efficient memory usage during batch processing
436pub const MAX_MESSAGE_BYTES: usize = 65_536; // 64 KiB
437
438/// Maximum number of messages per receive call
439///
440/// This limit balances throughput with processing simplicity:
441/// - **AWS SQS**: Supports up to 10 messages per ReceiveMessage call
442/// - **Azure Service Bus**: Can receive multiple messages via prefetch/batching
443/// - **GCP Pub/Sub**: Supports configurable max_messages per Pull request
444///
445/// The 10-message limit ensures:
446/// - Portable batch sizes across all backends
447/// - Manageable memory usage
448/// - Reasonable processing latency per batch
449pub const MAX_BATCH_SIZE: usize = 10;
450
451/// Fixed lease duration in seconds
452///
453/// Messages are leased for exactly 30 seconds after delivery:
454/// - Long enough for most processing tasks
455/// - Short enough to enable fast retry on failures
456/// - Eliminates complexity of dynamic lease management
457/// - Consistent across all platforms
458pub const LEASE_SECONDS: u64 = 30;
459
460/// A trait for queue bindings providing minimal, portable queue operations.
461#[async_trait]
462pub trait Queue: Binding {
463    /// Send a message to the specified queue
464    async fn send(&self, queue: &str, message: MessagePayload) -> Result<()>;
465
466    /// Receive up to `max_messages` (1..=10) from the specified queue
467    async fn receive(&self, queue: &str, max_messages: usize) -> Result<Vec<QueueMessage>>;
468
469    /// Acknowledge a message using its receipt handle (idempotent)
470    async fn ack(&self, queue: &str, receipt_handle: &str) -> Result<()>;
471}
472
473/// Request for invoking a function directly
474#[derive(Debug, Clone, Serialize, Deserialize)]
475#[serde(rename_all = "camelCase")]
476#[cfg_attr(feature = "openapi", derive(ToSchema))]
477pub struct FunctionInvokeRequest {
478    /// Function identifier (name, ARN, URL, etc.)
479    pub target_function: String,
480    /// HTTP method
481    pub method: String,
482    /// Request path
483    pub path: String,
484    /// HTTP headers
485    pub headers: BTreeMap<String, String>,
486    /// Request body bytes
487    pub body: Vec<u8>,
488    /// Optional timeout for the invocation
489    pub timeout: Option<Duration>,
490}
491
492/// Response from function invocation
493#[derive(Debug, Clone, Serialize, Deserialize)]
494#[serde(rename_all = "camelCase")]
495#[cfg_attr(feature = "openapi", derive(ToSchema))]
496pub struct FunctionInvokeResponse {
497    /// HTTP status code
498    pub status: u16,
499    /// HTTP response headers
500    pub headers: BTreeMap<String, String>,
501    /// Response body bytes
502    pub body: Vec<u8>,
503}
504
505/// A trait for function bindings that enable direct function-to-function calls
506#[async_trait]
507pub trait Function: Binding {
508    /// Invoke a function with HTTP request data.
509    ///
510    /// This enables direct, low-latency function-to-function communication within
511    /// the same cloud environment, bypassing ARC for internal calls.
512    ///
513    /// Platform implementations:
514    /// - AWS: Uses InvokeFunction API directly
515    /// - GCP: Calls private service URL directly  
516    /// - Azure: Calls private container app URL directly
517    /// - Kubernetes: HTTP call to internal service
518    async fn invoke(&self, request: FunctionInvokeRequest) -> Result<FunctionInvokeResponse>;
519
520    /// Get the public URL of the function, if available.
521    ///
522    /// Returns the function's public URL if it exists and is accessible.
523    /// This is useful for exposing public endpoints or getting URLs for
524    /// external integration.
525    ///
526    /// Platform implementations:
527    /// - AWS: Uses GetFunctionUrlConfig API or returns URL from binding
528    /// - GCP: Returns Cloud Run service URL or calls get_service API
529    /// - Azure: Returns Container App URL or calls get_container_app API
530    async fn get_function_url(&self) -> Result<Option<String>>;
531
532    /// Get a reference to this object as `Any` for dynamic casting
533    fn as_any(&self) -> &dyn std::any::Any;
534}
535
536/// A trait for container bindings that enable container-to-container communication
537#[async_trait]
538pub trait Container: Binding {
539    /// Get the internal URL for container-to-container communication.
540    ///
541    /// This returns the internal service discovery URL that other containers
542    /// in the same network can use to communicate with this container.
543    ///
544    /// Platform implementations:
545    /// - Horizon (AWS/GCP/Azure): Returns internal DNS URL (e.g., "http://api.svc:8080")
546    /// - Local (Docker): Returns Docker network DNS URL (e.g., "http://api.svc:3000")
547    fn get_internal_url(&self) -> &str;
548
549    /// Get the public URL of the container, if available.
550    ///
551    /// Returns the container's public URL if it exists and is accessible
552    /// from outside the cluster/network.
553    ///
554    /// Platform implementations:
555    /// - Horizon: Returns load balancer URL if exposed publicly
556    /// - Local: Returns localhost URL with mapped port (e.g., "http://localhost:62844")
557    fn get_public_url(&self) -> Option<&str>;
558
559    /// Get the container name/ID.
560    fn get_container_name(&self) -> &str;
561
562    /// Get a reference to this object as `Any` for dynamic casting
563    fn as_any(&self) -> &dyn std::any::Any;
564}
565
566/// A provider must implement methods to load the various types of bindings
567/// based on environment variables or other configuration sources.
568#[async_trait]
569pub trait BindingsProviderApi: Send + Sync + std::fmt::Debug {
570    /// Given a binding identifier, builds a Storage implementation.
571    async fn load_storage(&self, binding_name: &str) -> Result<Arc<dyn Storage>>;
572
573    /// Given a binding identifier, builds a Build implementation.
574    async fn load_build(&self, binding_name: &str) -> Result<Arc<dyn Build>>;
575
576    /// Given a binding identifier, builds an ArtifactRegistry implementation.
577    async fn load_artifact_registry(&self, binding_name: &str)
578        -> Result<Arc<dyn ArtifactRegistry>>;
579
580    /// Given a binding identifier, builds a Vault implementation.
581    async fn load_vault(&self, binding_name: &str) -> Result<Arc<dyn Vault>>;
582
583    /// Given a binding identifier, builds a KV implementation.
584    async fn load_kv(&self, binding_name: &str) -> Result<Arc<dyn Kv>>;
585
586    /// Given a binding identifier, builds a Queue implementation.
587    async fn load_queue(&self, binding_name: &str) -> Result<Arc<dyn Queue>>;
588
589    /// Given a binding identifier, builds a Function implementation.
590    async fn load_function(&self, binding_name: &str) -> Result<Arc<dyn Function>>;
591
592    /// Given a binding identifier, builds a Container implementation.
593    async fn load_container(&self, binding_name: &str) -> Result<Arc<dyn Container>>;
594
595    /// Given a binding identifier, builds a ServiceAccount implementation.
596    async fn load_service_account(&self, binding_name: &str) -> Result<Arc<dyn ServiceAccount>>;
597}