1use crate::prelude::*;
2use proc_macro2::Span;
3use std::rc::Rc;
4use syn::*;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum Direction
8{
9 In,
10 Out,
11 Retval,
12}
13
14#[derive(PartialEq, Eq, Debug, Hash)]
15pub struct ModelTypeSystemConfig
16{
17 pub effective_system: ModelTypeSystem,
18 pub is_default: bool,
19}
20
21impl ModelTypeSystemConfig
22{
23 pub fn get_unique_name(&self, base: &str) -> String
24 {
25 match self.is_default {
26 true => base.to_string(),
27 false => format!("{}_{:?}", base, self.effective_system),
28 }
29 }
30}
31
32#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
33pub enum ModelTypeSystem
34{
35 Automation,
37
38 Raw,
40}
41
42impl ModelTypeSystem
43{
44 pub fn as_tokens(self) -> TokenStream
46 {
47 match self {
48 ModelTypeSystem::Automation => quote!(Automation),
49 ModelTypeSystem::Raw => quote!(Raw),
50 }
51 }
52
53 pub fn as_typesystem_tokens(self, span: Span) -> TokenStream
55 {
56 match self {
57 ModelTypeSystem::Automation => {
58 quote_spanned!(span=> intercom::type_system::TypeSystemName::Automation)
59 }
60 ModelTypeSystem::Raw => {
61 quote_spanned!(span=> intercom::type_system::TypeSystemName::Raw)
62 }
63 }
64 }
65
66 pub fn as_typesystem_type(self, span: Span) -> Type
68 {
69 syn::parse2(match self {
70 ModelTypeSystem::Automation => {
71 quote_spanned!(span=> intercom::type_system::AutomationTypeSystem)
72 }
73 ModelTypeSystem::Raw => quote_spanned!(span=> intercom::type_system::RawTypeSystem),
74 })
75 .unwrap()
76 }
77}
78
79impl quote::IdentFragment for ModelTypeSystem
80{
81 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result
82 {
83 write!(f, "{:?}", self)
84 }
85}
86
87pub struct TypeContext
89{
90 type_system: ModelTypeSystem,
91}
92
93impl TypeContext
94{
95 pub fn new(type_system: ModelTypeSystem) -> TypeContext
96 {
97 TypeContext { type_system }
98 }
99}
100
101pub struct TypeHandler
104{
105 ty: Type,
106 context: TypeContext,
107}
108
109impl TypeHandler
110{
111 pub fn rust_ty(&self) -> Type
113 {
114 self.ty.clone()
115 }
116
117 pub fn com_ty(&self, span: Span) -> Type
119 {
120 let ty = &self.ty;
122 let ts = self.context.type_system.as_typesystem_type(span);
123 syn::parse2(
124 quote_spanned!(span => <#ty as intercom::type_system::ExternType<#ts>>::ForeignType),
125 )
126 .unwrap()
127 }
128
129 pub fn com_to_rust(
131 &self,
132 ident: &Ident,
133 span: Span,
134 dir: Direction,
135 infallible: bool,
136 ) -> TokenStream
137 {
138 let ty = &self.ty;
140 let ts = self.context.type_system.as_typesystem_type(span);
141 let (tr, unwrap) = resolve_type_handling(dir, infallible, span);
142 let (maybe_ref, maybe_as_ref) = resolve_ref(ty);
143 match dir {
144 Direction::In => quote_spanned!(span=>
145 #maybe_ref <#ty as #tr<#ts>>
146 ::from_foreign_parameter(#ident)#unwrap#maybe_as_ref),
147
148 Direction::Out | Direction::Retval => quote_spanned!(span=>
152 <#ty as #tr<#ts>>::from_foreign_output(#ident)),
153 }
154 }
155
156 pub fn rust_to_com(
158 &self,
159 ident: &Ident,
160 span: Span,
161 dir: Direction,
162 infallible: bool,
163 ) -> TokenStream
164 {
165 let ty = &self.ty;
167 let ts = self.context.type_system.as_typesystem_type(span);
168 let (tr, unwrap) = resolve_type_handling(dir, infallible, span);
169 match dir {
170 Direction::In => quote_spanned!(span=>
171 <#ty as #tr<#ts>>
172 ::into_foreign_parameter(#ident)#unwrap.0),
173 Direction::Out | Direction::Retval => quote_spanned!(span=>
174 <#ty as #tr<#ts>>
175 ::into_foreign_output(#ident)#unwrap),
176 }
177 }
178
179 pub fn default_value(&self) -> TokenStream
181 {
182 quote!(intercom::type_system::ExternDefault::extern_default())
183 }
184}
185
186fn resolve_ref(ty: &Type) -> (TokenStream, TokenStream)
187{
188 if let syn::Type::Reference(..) = ty {
189 return (quote!(&), quote!());
190 }
191
192 if has_ref(ty) {
193 (quote!(), quote!(.as_ref()))
194 } else {
195 (quote!(), quote!())
196 }
197}
198
199fn has_ref(ty: &Type) -> bool
200{
201 match ty {
202 syn::Type::Reference(..) => true,
203 syn::Type::Path(p) => {
204 let last_segment = p.path.segments.last().expect("Path was empth");
205 match &last_segment.arguments {
206 syn::PathArguments::None | syn::PathArguments::Parenthesized(..) => false,
207 syn::PathArguments::AngleBracketed(generics) => {
208 generics.args.iter().any(|g| match g {
209 syn::GenericArgument::Type(t) => has_ref(t),
210 _ => false,
211 })
212 }
213 }
214 }
215 _ => false,
216 }
217}
218
219fn resolve_type_handling(dir: Direction, infallible: bool, span: Span)
220 -> (TokenStream, TokenStream)
221{
222 let dir_part = match dir {
223 Direction::In => "Input",
224 Direction::Out | Direction::Retval => "Output",
225 };
226 let (fallibility, unwrap) = match infallible {
227 true => ("Infallible", quote!()),
228 false => ("", quote!(?)),
229 };
230
231 let ident = Ident::new(&format!("{}Extern{}", fallibility, dir_part), span);
232 (
233 quote_spanned!(span => intercom::type_system::#ident),
234 unwrap,
235 )
236}
237
238pub fn get_ty_handler(arg_ty: &Type, context: TypeContext) -> Rc<TypeHandler>
240{
241 Rc::new(TypeHandler {
242 ty: arg_ty.clone(),
243 context,
244 })
245}