1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use crate::blueprint::constructor::{CloningStrategy, Lifecycle, RegisteredConstructor};
use crate::blueprint::conversions::{lint2lint, raw_identifiers2callable};
use crate::blueprint::linter::Lint;
use crate::blueprint::reflection::RawIdentifiers;
use crate::blueprint::Blueprint;
use pavex_bp_schema::{Callable, LintSetting};
use std::collections::BTreeMap;

/// A constructor that has been configured but has not yet been registered with a [`Blueprint`].
///
/// # Guide
///
/// Check out the ["Dependency injection"](https://pavex.dev/docs/guide/dependency_injection)
/// section of Pavex's guide for a thorough introduction to dependency injection
/// in Pavex applications.
///
/// # Use cases
///
/// [`Constructor`] is primarily used by kits (e.g. [`ApiKit`](crate::kit::ApiKit))
/// to allow users to customize (or disable!)
/// the bundled constructors **before** registering them with a [`Blueprint`].
#[derive(Clone, Debug)]
pub struct Constructor {
    pub(in crate::blueprint) callable: Callable,
    pub(in crate::blueprint) lifecycle: Lifecycle,
    pub(in crate::blueprint) cloning_strategy: Option<CloningStrategy>,
    pub(in crate::blueprint) error_handler: Option<Callable>,
    pub(in crate::blueprint) lints: BTreeMap<pavex_bp_schema::Lint, LintSetting>,
}

impl Constructor {
    /// Create a new (unregistered) constructor.
    ///
    /// Check out the documentation of [`Blueprint::constructor`] for more details
    /// on constructors.
    #[track_caller]
    pub fn new(callable: RawIdentifiers, lifecycle: Lifecycle) -> Self {
        Self {
            callable: raw_identifiers2callable(callable),
            lifecycle,
            cloning_strategy: None,
            error_handler: None,
            lints: Default::default(),
        }
    }

    /// Create a new (unregistered) constructor with a [singleton](Lifecycle::Singleton) lifecycle.
    ///
    /// It's a shorthand for [`Constructor::new(callable, Lifecycle::Singleton)`](Constructor::new).
    #[track_caller]
    pub fn singleton(callable: RawIdentifiers) -> Self {
        Constructor::new(callable, Lifecycle::Singleton)
    }

    /// Create a new (unregistered) constructor with a [request-scoped](Lifecycle::RequestScoped) lifecycle.
    ///
    /// It's a shorthand for [`Constructor::new(callable, Lifecycle::RequestScoped)`](Constructor::new).
    #[track_caller]
    pub fn request_scoped(callable: RawIdentifiers) -> Self {
        Constructor::new(callable, Lifecycle::RequestScoped)
    }

    /// Create a new (unregistered) constructor with a [transient](Lifecycle::Transient) lifecycle.
    ///
    /// It's a shorthand for [`Constructor::new(callable, Lifecycle::Transient)`](Constructor::new).
    #[track_caller]
    pub fn transient(callable: RawIdentifiers) -> Self {
        Constructor::new(callable, Lifecycle::Transient)
    }

    /// Register an error handler for this constructor.
    ///
    /// Check out the documentation of [`RegisteredConstructor::error_handler`] for more details.
    #[track_caller]
    pub fn error_handler(mut self, error_handler: RawIdentifiers) -> Self {
        self.error_handler = Some(raw_identifiers2callable(error_handler));
        self
    }

    /// Set the cloning strategy for the output type returned by this constructor.
    ///
    /// Check out the documentation of [`CloningStrategy`] for more details.
    pub fn cloning(mut self, cloning_strategy: CloningStrategy) -> Self {
        self.cloning_strategy = Some(cloning_strategy);
        self
    }

    /// Set the cloning strategy to [`CloningStrategy::CloneIfNecessary`].  
    /// Check out [`Constructor::cloning`] for more details.
    pub fn clone_if_necessary(self) -> Self {
        self.cloning(CloningStrategy::CloneIfNecessary)
    }

    /// Set the cloning strategy to [`CloningStrategy::NeverClone`].  
    /// Check out [`Constructor::cloning`] for more details.
    pub fn never_clone(self) -> Self {
        self.cloning(CloningStrategy::NeverClone)
    }

    /// Tell Pavex to ignore a specific [`Lint`] when analysing
    /// this constructor and the way it's used.
    pub fn ignore(mut self, lint: Lint) -> Self {
        self.lints.insert(lint2lint(lint), LintSetting::Ignore);
        self
    }

    /// Tell Pavex to enforce a specific [`Lint`] when analysing
    /// this constructor and the way it's used.
    pub fn enforce(mut self, lint: Lint) -> Self {
        self.lints.insert(lint2lint(lint), LintSetting::Enforce);
        self
    }

    /// Register this constructor with a [`Blueprint`].
    ///
    /// Check out the documentation of [`Blueprint::constructor`] for more details.
    pub fn register(self, bp: &mut Blueprint) -> RegisteredConstructor {
        bp.register_constructor(self)
    }
}