kunobi_decision/system_one.rs
1//! The [`SystemOne`] trait, so code can depend on "something that answers questions".
2
3use std::future::Future;
4use std::pin::Pin;
5use std::sync::Arc;
6
7use crate::answers::SystemOneResult;
8use crate::client::Client;
9use crate::error::Result;
10use crate::request::SystemOneRequest;
11
12/// The future returned by [`SystemOne::ask`].
13pub type AskFuture<'a> = Pin<Box<dyn Future<Output = Result<SystemOneResult>> + Send + 'a>>;
14
15/// Answers a [`SystemOneRequest`].
16///
17/// The trait carries only the request. Per-call options such as timeouts and retries
18/// belong to the implementation: configure them on the [`Client`] you pass in.
19///
20/// Take `&dyn SystemOne` or `impl SystemOne` in product code instead of [`Client`],
21/// and pass a fake in tests; see [`FakeSystemOne`](crate::testing::FakeSystemOne)
22/// behind the `testing` feature.
23///
24/// ```
25/// use kunobi_decision::{Questions, Result, SystemOne, SystemOneRequest, noul};
26///
27/// async fn is_billing(jev: &dyn SystemOne, ticket: &str) -> Result<bool> {
28/// let mut questions = Questions::new();
29/// let billing = questions.add("billing", noul("Is this ticket about billing?"));
30/// let result = jev.ask(SystemOneRequest::new(ticket, questions)).await?;
31/// Ok(result.answer(&billing)?.is_yes(0.7))
32/// }
33/// ```
34pub trait SystemOne: Send + Sync {
35 /// Answer the request with the implementation's default options.
36 fn ask(&self, request: SystemOneRequest) -> AskFuture<'_>;
37}
38
39impl SystemOne for Client {
40 fn ask(&self, request: SystemOneRequest) -> AskFuture<'_> {
41 Box::pin(std::future::IntoFuture::into_future(
42 self.system_one(request),
43 ))
44 }
45}
46
47impl<T: SystemOne + ?Sized> SystemOne for Arc<T> {
48 fn ask(&self, request: SystemOneRequest) -> AskFuture<'_> {
49 (**self).ask(request)
50 }
51}
52
53impl<T: SystemOne + ?Sized> SystemOne for Box<T> {
54 fn ask(&self, request: SystemOneRequest) -> AskFuture<'_> {
55 (**self).ask(request)
56 }
57}
58
59impl<T: SystemOne + ?Sized> SystemOne for &T {
60 fn ask(&self, request: SystemOneRequest) -> AskFuture<'_> {
61 (**self).ask(request)
62 }
63}