refactor: split out testcontainers-rs-ext

This commit is contained in:
2025-04-05 19:51:59 +08:00
parent a3609696c7
commit 376d2b28d3
12 changed files with 1202 additions and 1443 deletions

View File

@@ -1,6 +1,4 @@
pub mod app;
pub mod fetch;
pub mod mikan;
#[cfg(feature = "testcontainers")]
pub mod testcontainers;
pub mod tracing;

View File

@@ -1,117 +0,0 @@
use async_trait::async_trait;
use bollard::container::ListContainersOptions;
use itertools::Itertools;
use testcontainers::{
ContainerRequest, Image, ImageExt, TestcontainersError,
core::logs::consumer::logging_consumer::LoggingConsumer,
};
pub const TESTCONTAINERS_PROJECT_KEY: &str = "tech.enfw.testcontainers.project";
pub const TESTCONTAINERS_CONTAINER_KEY: &str = "tech.enfw.testcontainers.container";
pub const TESTCONTAINERS_PRUNE_KEY: &str = "tech.enfw.testcontainers.prune";
#[async_trait]
pub trait ContainerRequestEnhancedExt<I>: Sized + ImageExt<I>
where
I: Image,
{
async fn with_prune_existed_label(
self,
container_label: &str,
prune: bool,
force: bool,
) -> Result<Self, TestcontainersError>;
fn with_default_log_consumer(self) -> Self;
}
#[async_trait]
impl<I> ContainerRequestEnhancedExt<I> for ContainerRequest<I>
where
I: Image,
{
async fn with_prune_existed_label(
self,
container_label: &str,
prune: bool,
force: bool,
) -> Result<Self, TestcontainersError> {
use std::collections::HashMap;
use bollard::container::PruneContainersOptions;
use testcontainers::core::client::docker_client_instance;
if prune {
let client = docker_client_instance().await?;
let mut filters = HashMap::<String, Vec<String>>::new();
filters.insert(
String::from("label"),
vec![
format!("{TESTCONTAINERS_PRUNE_KEY}=true"),
format!("{}={}", TESTCONTAINERS_PROJECT_KEY, "konobangu"),
format!("{}={}", TESTCONTAINERS_CONTAINER_KEY, container_label),
],
);
if force {
let result = client
.list_containers(Some(ListContainersOptions {
all: false,
filters: filters.clone(),
..Default::default()
}))
.await
.map_err(|err| TestcontainersError::Other(Box::new(err)))?;
let remove_containers = result
.iter()
.filter(|c| matches!(c.state.as_deref(), Some("running")))
.flat_map(|c| c.id.as_deref())
.collect_vec();
futures::future::try_join_all(
remove_containers
.iter()
.map(|c| client.stop_container(c, None)),
)
.await
.map_err(|error| TestcontainersError::Other(Box::new(error)))?;
if !remove_containers.is_empty() {
tracing::warn!(name = "stop running containers", result = ?remove_containers);
}
}
let result = client
.prune_containers(Some(PruneContainersOptions { filters }))
.await
.map_err(|err| TestcontainersError::Other(Box::new(err)))?;
if result
.containers_deleted
.as_ref()
.is_some_and(|c| !c.is_empty())
{
tracing::warn!(name = "prune existed containers", result = ?result);
}
}
let result = self.with_labels([
(TESTCONTAINERS_PRUNE_KEY, "true"),
(TESTCONTAINERS_PROJECT_KEY, "konobangu"),
(TESTCONTAINERS_CONTAINER_KEY, container_label),
]);
Ok(result)
}
fn with_default_log_consumer(self) -> Self {
self.with_log_consumer(
LoggingConsumer::new()
.with_stdout_level(log::Level::Info)
.with_stderr_level(log::Level::Error),
)
}
}