grass/dev/public/
strategy.rs

1use thiserror::Error;
2use tracing::info;
3
4use crate::dev::{
5    config,
6    strategy::{
7        alias::LocalAliasStrategy,
8        api::{LocalApiStrategy, MockApiStrategy},
9        discovery::LocalDiscoveryStrategy,
10        git::LocalGitStrategy,
11        path::LocalPathStrategy,
12    },
13};
14
15#[derive(Debug)]
16pub struct Api<T>(T);
17
18pub trait AccessApi<T> {
19    fn get_strategy(&self) -> &T;
20}
21
22impl<T> Api<T> {
23    pub fn new(value: T) -> Self {
24        Self(value)
25    }
26}
27
28impl<T> From<T> for Api<T> {
29    fn from(value: T) -> Self {
30        Self(value)
31    }
32}
33
34impl<T: Clone> From<&T> for Api<T> {
35    fn from(value: &T) -> Self {
36        Self(value.clone())
37    }
38}
39
40impl<T> AccessApi<T> for Api<T> {
41    fn get_strategy(&self) -> &T {
42        &self.0
43    }
44}
45
46#[macro_export]
47macro_rules! support_strategy {
48    ($trait_name:ident, $method_name:ident, $strategy_type:ident) => {
49        pub trait $trait_name {
50            type Strategy: $strategy_type;
51
52            fn $method_name(&self) -> &Self::Strategy;
53        }
54
55        impl<T> $crate::dev::public::strategy::Api<T>
56        where
57            T: $trait_name,
58        {
59            pub(crate) fn $method_name(&self) -> &T::Strategy {
60                $crate::dev::public::strategy::AccessApi::get_strategy(self).$method_name()
61            }
62        }
63    };
64}
65
66#[derive(Error, Debug)]
67pub enum LocalStrategyError {
68    #[error("Could not load user configuration\nReason: {reason}")]
69    LoadConfigError { reason: String },
70}
71
72/// Builds the API strategy using the default local config
73///
74/// The base configuration directory is located using [dirs::config_dir].
75/// All files under the `{config}/grass/` that end with `.toml` are then considered.
76/// They are considered in alphabetical order, with later values overriding earlier ones.
77pub fn use_local_strategy_with_default_config<T, U>(closure: T) -> Result<U, LocalStrategyError>
78where
79    T: Fn(Api<LocalApiStrategy>) -> Result<U, LocalStrategyError>,
80{
81    let config = config::load_user_config()
82        .map_err(|error| LocalStrategyError::LoadConfigError {
83            reason: error.to_string(),
84        })?
85        .grass;
86
87    let alias_strategy = LocalAliasStrategy::new(&config);
88    let path_strategy = LocalPathStrategy::new(&config);
89    let discovery_strategy = LocalDiscoveryStrategy::new(&config, &path_strategy);
90    let git_strategy = LocalGitStrategy::new(&path_strategy);
91
92    let api_strategy = LocalApiStrategy::new(
93        &alias_strategy,
94        &discovery_strategy,
95        &git_strategy,
96        &path_strategy,
97    );
98
99    info!("Using local strategy with default config");
100    closure(api_strategy.into())
101}
102
103/// Builds the API strategy using the mocking strategy
104///
105/// This can be used during testing.
106pub fn use_mock_strategy() -> Api<MockApiStrategy> {
107    Api(MockApiStrategy::default())
108}