use crate::command::{CommandExecutor, CommandOutput, DockerCommand};
use crate::error::Result;
use async_trait::async_trait;
#[derive(Debug, Clone)]
pub struct BuildxCreateResult {
pub name: String,
pub output: String,
pub success: bool,
}
impl BuildxCreateResult {
fn parse(output: &CommandOutput) -> Self {
let name = output.stdout.trim().to_string();
Self {
name,
output: output.stdout.clone(),
success: output.success,
}
}
}
#[derive(Debug, Clone, Default)]
#[allow(clippy::struct_excessive_bools)]
pub struct BuildxCreateCommand {
context: Option<String>,
append: bool,
bootstrap: bool,
buildkitd_config: Option<String>,
buildkitd_flags: Option<String>,
driver: Option<String>,
driver_opts: Vec<String>,
leave: bool,
name: Option<String>,
node: Option<String>,
platforms: Vec<String>,
use_builder: bool,
pub executor: CommandExecutor,
}
impl BuildxCreateCommand {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn context(mut self, context: impl Into<String>) -> Self {
self.context = Some(context.into());
self
}
#[must_use]
pub fn append(mut self) -> Self {
self.append = true;
self
}
#[must_use]
pub fn bootstrap(mut self) -> Self {
self.bootstrap = true;
self
}
#[must_use]
pub fn buildkitd_config(mut self, config: impl Into<String>) -> Self {
self.buildkitd_config = Some(config.into());
self
}
#[must_use]
pub fn buildkitd_flags(mut self, flags: impl Into<String>) -> Self {
self.buildkitd_flags = Some(flags.into());
self
}
#[must_use]
pub fn driver(mut self, driver: impl Into<String>) -> Self {
self.driver = Some(driver.into());
self
}
#[must_use]
pub fn driver_opt(mut self, opt: impl Into<String>) -> Self {
self.driver_opts.push(opt.into());
self
}
#[must_use]
pub fn leave(mut self) -> Self {
self.leave = true;
self
}
#[must_use]
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
#[must_use]
pub fn node(mut self, node: impl Into<String>) -> Self {
self.node = Some(node.into());
self
}
#[must_use]
pub fn platform(mut self, platform: impl Into<String>) -> Self {
self.platforms.push(platform.into());
self
}
#[must_use]
pub fn use_builder(mut self) -> Self {
self.use_builder = true;
self
}
fn build_args(&self) -> Vec<String> {
let mut args = vec!["buildx".to_string(), "create".to_string()];
if self.append {
args.push("--append".to_string());
}
if self.bootstrap {
args.push("--bootstrap".to_string());
}
if let Some(ref config) = self.buildkitd_config {
args.push("--buildkitd-config".to_string());
args.push(config.clone());
}
if let Some(ref flags) = self.buildkitd_flags {
args.push("--buildkitd-flags".to_string());
args.push(flags.clone());
}
if let Some(ref driver) = self.driver {
args.push("--driver".to_string());
args.push(driver.clone());
}
for opt in &self.driver_opts {
args.push("--driver-opt".to_string());
args.push(opt.clone());
}
if self.leave {
args.push("--leave".to_string());
}
if let Some(ref name) = self.name {
args.push("--name".to_string());
args.push(name.clone());
}
if let Some(ref node) = self.node {
args.push("--node".to_string());
args.push(node.clone());
}
for platform in &self.platforms {
args.push("--platform".to_string());
args.push(platform.clone());
}
if self.use_builder {
args.push("--use".to_string());
}
if let Some(ref context) = self.context {
args.push(context.clone());
}
args.extend(self.executor.raw_args.clone());
args
}
}
#[async_trait]
impl DockerCommand for BuildxCreateCommand {
type Output = BuildxCreateResult;
fn get_executor(&self) -> &CommandExecutor {
&self.executor
}
fn get_executor_mut(&mut self) -> &mut CommandExecutor {
&mut self.executor
}
fn build_command_args(&self) -> Vec<String> {
self.build_args()
}
async fn execute(&self) -> Result<Self::Output> {
let args = self.build_args();
let output = self.execute_command(args).await?;
Ok(BuildxCreateResult::parse(&output))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_buildx_create_basic() {
let cmd = BuildxCreateCommand::new();
let args = cmd.build_args();
assert_eq!(args, vec!["buildx", "create"]);
}
#[test]
fn test_buildx_create_with_name() {
let cmd = BuildxCreateCommand::new().name("mybuilder");
let args = cmd.build_args();
assert!(args.contains(&"--name".to_string()));
assert!(args.contains(&"mybuilder".to_string()));
}
#[test]
fn test_buildx_create_with_driver() {
let cmd = BuildxCreateCommand::new().driver("docker-container");
let args = cmd.build_args();
assert!(args.contains(&"--driver".to_string()));
assert!(args.contains(&"docker-container".to_string()));
}
#[test]
fn test_buildx_create_with_platforms() {
let cmd = BuildxCreateCommand::new()
.platform("linux/amd64")
.platform("linux/arm64");
let args = cmd.build_args();
assert!(args.contains(&"--platform".to_string()));
assert!(args.contains(&"linux/amd64".to_string()));
assert!(args.contains(&"linux/arm64".to_string()));
}
#[test]
fn test_buildx_create_all_options() {
let cmd = BuildxCreateCommand::new()
.name("mybuilder")
.driver("docker-container")
.driver_opt("network=host")
.platform("linux/amd64")
.bootstrap()
.use_builder()
.append();
let args = cmd.build_args();
assert!(args.contains(&"--name".to_string()));
assert!(args.contains(&"--driver".to_string()));
assert!(args.contains(&"--driver-opt".to_string()));
assert!(args.contains(&"--platform".to_string()));
assert!(args.contains(&"--bootstrap".to_string()));
assert!(args.contains(&"--use".to_string()));
assert!(args.contains(&"--append".to_string()));
}
}