1use crate::{
2 context::Context,
3 function::{FunctionHandle, FunctionQuery, FunctionQueryParameter},
4 registry::{Registry, RegistryHandle},
5 types::TypeQuery,
6};
7use intuicio_data::data_stack::DataStackPack;
8use std::{cell::RefCell, marker::PhantomData, sync::Arc};
9use typid::ID;
10
11thread_local! {
12 static GLOBAL_HOST_STACK: RefCell<Vec<(HostId, Host)>> = const{ RefCell::new(vec![]) };
13}
14
15pub type HostId = ID<Host>;
16
17#[derive(Clone)]
18pub struct HostProducer {
19 producer: Arc<Box<dyn Fn() -> Host + Send + Sync>>,
20}
21
22impl HostProducer {
23 pub fn new(f: impl Fn() -> Host + Send + Sync + 'static) -> Self {
24 Self {
25 producer: Arc::new(Box::new(f)),
26 }
27 }
28
29 pub fn produce(&self) -> Host {
30 (self.producer)()
31 }
32}
33
34pub struct Host {
35 context: Context,
36 registry: RegistryHandle,
37}
38
39impl Host {
40 pub fn new(context: Context, registry: RegistryHandle) -> Self {
41 Self { context, registry }
42 }
43
44 pub fn fork(&self) -> Self {
45 Self {
46 context: self.context.fork(),
47 registry: self.registry.clone(),
48 }
49 }
50
51 #[allow(clippy::result_large_err)]
52 pub fn push_global(self) -> Result<HostId, Self> {
53 GLOBAL_HOST_STACK.with(|host| match host.try_borrow_mut() {
54 Ok(mut stack) => {
55 let id = HostId::new();
56 stack.push((id, self));
57 Ok(id)
58 }
59 Err(_) => Err(self),
60 })
61 }
62
63 pub fn pop_global() -> Option<Self> {
64 GLOBAL_HOST_STACK.with(move |stack| Some(stack.try_borrow_mut().ok()?.pop()?.1))
65 }
66
67 pub fn remove_global(id: HostId) -> Option<Self> {
68 GLOBAL_HOST_STACK.with(move |stack| {
69 let mut stack = stack.try_borrow_mut().ok()?;
70 let index = stack.iter().position(|(host_id, _)| host_id == &id)?;
71 Some(stack.remove(index).1)
72 })
73 }
74
75 pub fn with_global<T>(f: impl FnOnce(&mut Self) -> T) -> Option<T> {
76 GLOBAL_HOST_STACK.with(move |stack| {
77 let mut stack = stack.try_borrow_mut().ok()?;
78 let host = &mut stack.last_mut()?.1;
79 Some(f(host))
80 })
81 }
82
83 pub fn context(&mut self) -> &mut Context {
84 &mut self.context
85 }
86
87 pub fn registry(&self) -> &Registry {
88 &self.registry
89 }
90
91 pub fn context_and_registry(&mut self) -> (&mut Context, &Registry) {
92 (&mut self.context, &self.registry)
93 }
94
95 pub fn find_function(
96 &self,
97 name: &str,
98 module_name: &str,
99 type_name: Option<&str>,
100 ) -> Option<FunctionHandle> {
101 self.registry.find_function(FunctionQuery {
102 name: Some(name.into()),
103 module_name: Some(module_name.into()),
104 type_query: type_name.map(|type_name| TypeQuery {
105 name: Some(type_name.into()),
106 ..Default::default()
107 }),
108 ..Default::default()
109 })
110 }
111
112 pub fn call_function<O: DataStackPack, I: DataStackPack>(
113 &mut self,
114 name: &str,
115 module_name: &str,
116 type_name: Option<&str>,
117 ) -> Option<HostFunctionCall<I, O>> {
118 let inputs_query = I::pack_types()
119 .into_iter()
120 .map(|type_hash| FunctionQueryParameter {
121 type_query: Some(TypeQuery {
122 type_hash: Some(type_hash),
123 ..Default::default()
124 }),
125 ..Default::default()
126 })
127 .collect::<Vec<_>>();
128 let outputs_query = O::pack_types()
129 .into_iter()
130 .map(|type_hash| FunctionQueryParameter {
131 type_query: Some(TypeQuery {
132 type_hash: Some(type_hash),
133 ..Default::default()
134 }),
135 ..Default::default()
136 })
137 .collect::<Vec<_>>();
138 let handle = self.registry.find_function(FunctionQuery {
139 name: Some(name.into()),
140 module_name: Some(module_name.into()),
141 type_query: type_name.map(|type_name| TypeQuery {
142 name: Some(type_name.into()),
143 ..Default::default()
144 }),
145 inputs: inputs_query.into(),
146 outputs: outputs_query.into(),
147 ..Default::default()
148 })?;
149 Some(HostFunctionCall {
150 context: &mut self.context,
151 registry: &self.registry,
152 handle,
153 _phantom: Default::default(),
154 })
155 }
156}
157
158pub struct HostFunctionCall<'a, I: DataStackPack, O: DataStackPack> {
159 context: &'a mut Context,
160 registry: &'a Registry,
161 handle: FunctionHandle,
162 _phantom: PhantomData<(I, O)>,
163}
164
165impl<I: DataStackPack, O: DataStackPack> HostFunctionCall<'_, I, O> {
166 pub fn run(self, inputs: I) -> O {
167 self.handle.call(self.context, self.registry, inputs, false)
168 }
169}