1#![allow(non_snake_case)]
2use crate::{Adapter, DefaultModel, Model, NullAdapter, Result};
3
4#[cfg(not(target_arch = "wasm32"))]
5use crate::FileAdapter;
6
7use async_trait::async_trait;
8use rhai::{serde::to_dynamic, Dynamic};
9use serde::Serialize;
10
11use std::{
12 collections::hash_map::DefaultHasher,
13 hash::{Hash, Hasher},
14};
15
16#[async_trait]
17pub trait TryIntoModel: Send + Sync {
18 async fn try_into_model(self) -> Result<Box<dyn Model>>;
19}
20
21#[async_trait]
22pub trait TryIntoAdapter: Send + Sync {
23 async fn try_into_adapter(self) -> Result<Box<dyn Adapter>>;
24}
25
26#[async_trait]
27impl TryIntoModel for &'static str {
28 async fn try_into_model(self) -> Result<Box<dyn Model>> {
29 #[cfg(not(target_arch = "wasm32"))]
30 {
31 Ok(Box::new(DefaultModel::from_file(self).await?))
32 }
33 #[cfg(target_arch = "wasm32")]
34 {
35 Ok(Box::new(DefaultModel::from_str(self).await?))
36 }
37 }
38}
39
40#[async_trait]
41impl<T> TryIntoModel for Option<T>
42where
43 T: TryIntoModel,
44{
45 #[allow(clippy::box_default)]
46 async fn try_into_model(self) -> Result<Box<dyn Model>> {
47 if let Some(m) = self {
48 m.try_into_model().await
49 } else {
50 Ok(Box::new(DefaultModel::default()))
51 }
52 }
53}
54
55#[async_trait]
56impl TryIntoAdapter for &'static str {
57 async fn try_into_adapter(self) -> Result<Box<dyn Adapter>> {
58 #[cfg(not(target_arch = "wasm32"))]
59 {
60 Ok(Box::new(FileAdapter::new(self)))
61 }
62
63 #[cfg(target_arch = "wasm32")]
64 {
65 Ok(Box::new(NullAdapter))
66 }
67 }
68}
69
70#[async_trait]
71impl<T> TryIntoAdapter for Option<T>
72where
73 T: TryIntoAdapter,
74{
75 async fn try_into_adapter(self) -> Result<Box<dyn Adapter>> {
76 if let Some(a) = self {
77 a.try_into_adapter().await
78 } else {
79 Ok(Box::new(NullAdapter))
80 }
81 }
82}
83
84#[allow(clippy::unit_arg)]
85#[async_trait]
86impl TryIntoAdapter for () {
87 async fn try_into_adapter(self) -> Result<Box<dyn Adapter>> {
88 Ok(Box::new(NullAdapter))
89 }
90}
91
92#[async_trait]
93impl<T> TryIntoModel for T
94where
95 T: Model + 'static,
96{
97 async fn try_into_model(self) -> Result<Box<dyn Model>> {
98 Ok(Box::new(self))
99 }
100}
101
102#[async_trait]
103impl<T> TryIntoAdapter for T
104where
105 T: Adapter + 'static,
106{
107 async fn try_into_adapter(self) -> Result<Box<dyn Adapter>> {
108 Ok(Box::new(self))
109 }
110}
111
112pub trait EnforceArgs {
113 fn try_into_vec(self) -> Result<Vec<Dynamic>>;
114 fn cache_key(&self) -> u64;
115}
116
117impl<T> EnforceArgs for Vec<T>
118where
119 T: Into<Dynamic> + Hash + Clone,
120{
121 fn try_into_vec(self) -> Result<Vec<Dynamic>> {
122 Ok(self.into_iter().map(|x| x.into()).collect())
123 }
124
125 fn cache_key(&self) -> u64 {
126 let mut hasher = DefaultHasher::new();
127 self.hash(&mut hasher);
128 hasher.finish()
129 }
130}
131
132macro_rules! impl_args {
133 ($($p:ident),*) => {
134 impl<$($p: Serialize + Hash),*> EnforceArgs for ($($p,)*)
135 {
136 fn try_into_vec(self) -> Result<Vec<Dynamic>> {
137 let ($($p,)*) = self;
138 let _v = vec![$(to_dynamic($p)?,)*];
139
140 Ok(_v)
141 }
142
143 fn cache_key(&self) -> u64 {
144 let ($($p,)*) = self;
145 let mut _hasher = DefaultHasher::new();
146
147 $($p.hash(&mut _hasher);)*
148
149 _hasher.finish()
150 }
151 }
152
153 impl_args!(@pop $($p),*);
154 };
155 (@pop) => {
156 };
157 (@pop $head:ident) => {
158 impl_args!();
159 };
160 (@pop $head:ident $(, $tail:ident)+) => {
161 impl_args!($($tail),*);
162 };
163}
164
165impl_args!(A, B, C, D, E, F, G, H, J, K, L, M, N, P, Q, R, S, T, U, V);