use crate::config::DebtmapConfig;
use crate::effects::{
asks_config, effect_from_fn, effect_pure, validation_failure, validation_success,
AnalysisEffect, AnalysisValidation,
};
use crate::env::{AnalysisEnv, RealEnv};
use crate::errors::AnalysisError;
use stillwater::effect::prelude::*;
use stillwater::Effect;
pub use crate::effects::{
combine_validations, run_effect, run_effect_async, run_effect_with_env, run_validation,
validation_failures,
};
pub fn analyze_with_config<T, F>(f: F) -> AnalysisEffect<T>
where
T: Send + 'static,
F: FnOnce(&DebtmapConfig) -> Result<T, AnalysisError> + Send + 'static,
{
effect_from_fn(move |env: &RealEnv| f(env.config()))
}
pub fn analyze_with_env<T, F>(f: F) -> AnalysisEffect<T>
where
T: Send + 'static,
F: FnOnce(&RealEnv) -> Result<T, AnalysisError> + Send + 'static,
{
effect_from_fn(f)
}
pub fn lift_pure<T: Send + 'static>(value: T) -> AnalysisEffect<T> {
effect_pure(value)
}
pub fn validate_result<T: Clone>(result: Result<T, AnalysisError>) -> AnalysisValidation<T> {
match result {
Ok(value) => validation_success(value),
Err(e) => validation_failure(e),
}
}
pub fn query_config<T, F>(f: F) -> impl Effect<Output = T, Error = AnalysisError, Env = RealEnv>
where
T: Send + 'static,
F: Fn(&DebtmapConfig) -> T + Send + Sync + 'static,
{
asks_config::<T, RealEnv, _>(f)
}
pub fn get_complexity_threshold() -> impl Effect<Output = u32, Error = AnalysisError, Env = RealEnv>
{
query_config(|config| {
config
.thresholds
.as_ref()
.and_then(|t| t.complexity)
.unwrap_or(10)
})
}
pub fn get_file_length_threshold(
) -> impl Effect<Output = usize, Error = AnalysisError, Env = RealEnv> {
query_config(|config| {
config
.thresholds
.as_ref()
.and_then(|t| t.max_file_length)
.unwrap_or(500)
})
}
pub fn get_cognitive_threshold() -> impl Effect<Output = u32, Error = AnalysisError, Env = RealEnv>
{
query_config(|config| {
config
.thresholds
.as_ref()
.and_then(|t| t.minimum_cognitive_complexity)
.unwrap_or(15)
})
}
pub fn sequence_effects<T>(effects: Vec<AnalysisEffect<T>>) -> AnalysisEffect<Vec<T>>
where
T: Send + 'static,
{
from_async(move |env: &RealEnv| {
let env = env.clone();
let effects = effects;
async move {
let mut results = Vec::new();
for effect in effects {
let result = effect.run(&env).await?;
results.push(result);
}
Ok(results)
}
})
.boxed()
}
pub fn traverse_effect<T, U, F>(items: Vec<T>, f: F) -> AnalysisEffect<Vec<U>>
where
T: Send + 'static,
U: Send + 'static,
F: Fn(T) -> AnalysisEffect<U> + Send + Sync + 'static,
{
from_async(move |env: &RealEnv| {
let env = env.clone();
async move {
let mut results = Vec::new();
for item in items {
let effect = f(item);
let result = effect.run(&env).await?;
results.push(result);
}
Ok(results)
}
})
.boxed()
}
pub fn run_analysis_effect<T: Send + 'static>(
effect: AnalysisEffect<T>,
config: DebtmapConfig,
) -> anyhow::Result<T> {
run_effect(effect, config)
}
pub fn run_analysis_effect_default<T: Send + 'static>(
effect: AnalysisEffect<T>,
) -> anyhow::Result<T> {
run_effect(effect, DebtmapConfig::default())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::ThresholdsConfig;
#[test]
fn test_lift_pure() {
let effect = lift_pure(42);
let result = run_analysis_effect_default(effect);
assert_eq!(result.unwrap(), 42);
}
#[test]
fn test_validate_result_success() {
let result: Result<i32, AnalysisError> = Ok(42);
let validation = validate_result(result);
assert!(validation.is_success());
}
#[test]
fn test_validate_result_failure() {
let result: Result<i32, AnalysisError> = Err(AnalysisError::validation("test error"));
let validation = validate_result(result);
assert!(validation.is_failure());
}
#[tokio::test]
async fn test_query_config_with_thresholds() {
let config = DebtmapConfig {
thresholds: Some(ThresholdsConfig {
complexity: Some(15),
..Default::default()
}),
..Default::default()
};
let env = RealEnv::new(config);
let effect = query_config(|c| {
c.thresholds
.as_ref()
.and_then(|t| t.complexity)
.unwrap_or(10)
});
let result = effect.run(&env).await.unwrap();
assert_eq!(result, 15);
}
#[tokio::test]
async fn test_get_complexity_threshold_default() {
let env = RealEnv::default();
let effect = get_complexity_threshold();
let result = effect.run(&env).await.unwrap();
assert_eq!(result, 10);
}
#[tokio::test]
async fn test_get_complexity_threshold_custom() {
let config = DebtmapConfig {
thresholds: Some(ThresholdsConfig {
complexity: Some(20),
..Default::default()
}),
..Default::default()
};
let env = RealEnv::new(config);
let effect = get_complexity_threshold();
let result = effect.run(&env).await.unwrap();
assert_eq!(result, 20);
}
#[tokio::test]
async fn test_analyze_with_config() {
let config = DebtmapConfig {
thresholds: Some(ThresholdsConfig {
complexity: Some(5),
..Default::default()
}),
..Default::default()
};
let env = RealEnv::new(config);
let effect = analyze_with_config(|config| {
let threshold = config
.thresholds
.as_ref()
.and_then(|t| t.complexity)
.unwrap_or(10);
Ok(threshold * 2)
});
let result = effect.run(&env).await.unwrap();
assert_eq!(result, 10); }
#[tokio::test]
async fn test_sequence_effects() {
let env = RealEnv::default();
let effects = vec![lift_pure(1), lift_pure(2), lift_pure(3)];
let effect = sequence_effects(effects);
let result = effect.run(&env).await.unwrap();
assert_eq!(result, vec![1, 2, 3]);
}
#[tokio::test]
async fn test_traverse_effect() {
let env = RealEnv::default();
let items = vec![1, 2, 3, 4, 5];
let effect = traverse_effect(items, |n| lift_pure(n * 2));
let result = effect.run(&env).await.unwrap();
assert_eq!(result, vec![2, 4, 6, 8, 10]);
}
}