Skip to main content

ironflow_ops_docker/networks/
connect.rs

1//! Network connection operations: connect, disconnect.
2
3use async_trait::async_trait;
4use bollard::Docker;
5use bollard::models::{NetworkConnectRequest, NetworkDisconnectRequest};
6use ironflow_core::error::OperationError;
7use ironflow_core::operation::{Operation, OperationContext, TypedOperation};
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10
11use crate::containers::DockerRef;
12use crate::helpers::{docker_error, to_value};
13
14// ---------------------------------------------------------------------------
15// NetworkConnect
16// ---------------------------------------------------------------------------
17
18/// Output of connecting a container to a network.
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct NetworkConnectOutput {
21    /// The network name.
22    pub network: String,
23    /// The container ID or name.
24    pub container: String,
25}
26
27/// Connect a container to a network.
28///
29/// # Examples
30///
31/// ```no_run
32/// use ironflow_ops_docker::networks::NetworkConnect;
33/// use ironflow_ops_docker::DockerClient;
34/// use ironflow_core::operation::Operation;
35///
36/// let client = DockerClient::connect_local().unwrap();
37/// let op = NetworkConnect::new(&client, "my-network", "my-container");
38/// assert_eq!(op.kind(), "docker");
39/// ```
40pub struct NetworkConnect {
41    docker: Docker,
42    network: String,
43    container: String,
44}
45
46impl NetworkConnect {
47    /// Create a new network-connect operation.
48    pub fn new(
49        client: impl Into<DockerRef>,
50        network: impl Into<String>,
51        container: impl Into<String>,
52    ) -> Self {
53        Self {
54            docker: client.into().0,
55            network: network.into(),
56            container: container.into(),
57        }
58    }
59
60    /// Execute and return a typed result.
61    ///
62    /// # Errors
63    ///
64    /// Returns [`OperationError::External`] if the network or container does
65    /// not exist.
66    pub async fn run(
67        &self,
68        _ctx: &OperationContext,
69    ) -> Result<NetworkConnectOutput, OperationError> {
70        let request = NetworkConnectRequest {
71            container: self.container.clone(),
72            ..Default::default()
73        };
74        self.docker
75            .connect_network(&self.network, request)
76            .await
77            .map_err(docker_error)?;
78        Ok(NetworkConnectOutput {
79            network: self.network.clone(),
80            container: self.container.clone(),
81        })
82    }
83}
84
85#[async_trait]
86impl Operation for NetworkConnect {
87    fn kind(&self) -> &str {
88        "docker"
89    }
90
91    async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
92        to_value(&self.run(ctx).await?)
93    }
94
95    fn input(&self) -> Option<Value> {
96        Some(serde_json::json!({
97            "operation": "network_connect",
98            "network": self.network,
99            "container": self.container,
100        }))
101    }
102}
103
104impl TypedOperation for NetworkConnect {
105    type Output = NetworkConnectOutput;
106}
107
108// ---------------------------------------------------------------------------
109// NetworkDisconnect
110// ---------------------------------------------------------------------------
111
112/// Output of disconnecting a container from a network.
113#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct NetworkDisconnectOutput {
115    /// The network name.
116    pub network: String,
117    /// The container ID or name.
118    pub container: String,
119}
120
121/// Disconnect a container from a network.
122///
123/// # Examples
124///
125/// ```no_run
126/// use ironflow_ops_docker::networks::NetworkDisconnect;
127/// use ironflow_ops_docker::DockerClient;
128/// use ironflow_core::operation::Operation;
129///
130/// let client = DockerClient::connect_local().unwrap();
131/// let op = NetworkDisconnect::new(&client, "my-network", "my-container");
132/// assert_eq!(op.kind(), "docker");
133/// ```
134pub struct NetworkDisconnect {
135    docker: Docker,
136    network: String,
137    container: String,
138}
139
140impl NetworkDisconnect {
141    /// Create a new network-disconnect operation.
142    pub fn new(
143        client: impl Into<DockerRef>,
144        network: impl Into<String>,
145        container: impl Into<String>,
146    ) -> Self {
147        Self {
148            docker: client.into().0,
149            network: network.into(),
150            container: container.into(),
151        }
152    }
153
154    /// Execute and return a typed result.
155    ///
156    /// # Errors
157    ///
158    /// Returns [`OperationError::External`] if the network or container does
159    /// not exist.
160    pub async fn run(
161        &self,
162        _ctx: &OperationContext,
163    ) -> Result<NetworkDisconnectOutput, OperationError> {
164        let request = NetworkDisconnectRequest {
165            container: self.container.clone(),
166            force: Some(false),
167        };
168        self.docker
169            .disconnect_network(&self.network, request)
170            .await
171            .map_err(docker_error)?;
172        Ok(NetworkDisconnectOutput {
173            network: self.network.clone(),
174            container: self.container.clone(),
175        })
176    }
177}
178
179#[async_trait]
180impl Operation for NetworkDisconnect {
181    fn kind(&self) -> &str {
182        "docker"
183    }
184
185    async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
186        to_value(&self.run(ctx).await?)
187    }
188
189    fn input(&self) -> Option<Value> {
190        Some(serde_json::json!({
191            "operation": "network_disconnect",
192            "network": self.network,
193            "container": self.container,
194        }))
195    }
196}
197
198impl TypedOperation for NetworkDisconnect {
199    type Output = NetworkDisconnectOutput;
200}