1use std::cell::RefCell;
2use std::fmt::Formatter;
3use std::rc::Rc;
4use syn::{Attribute, Item, ItemType, parse_quote, Path, TraitBound, Type, TypeBareFn, TypePath, TypeTraitObject, ItemTrait};
5use crate::ast::{CommaPunctuated, Depunctuated};
6use crate::composable::TraitModelPart1;
7use crate::composer::{ComposerLink, MaybeMacroLabeled};
8use crate::context::{GlobalContext, ScopeChain, ScopeSearch, ScopeSearchKey};
9use crate::kind::{ObjectKind, ScopeItemKind, SpecialType, TypeModelKind};
10use crate::ext::{DictionaryType, extract_trait_names, FermentableDictionaryType, ToType, AsType, Resolve, ResolveTrait, LifetimeProcessor, MaybeLambdaArgs, MaybeTraitBound};
11use crate::lang::Specification;
12use crate::presentation::{FFIFullDictionaryPath, FFIFullPath};
13use crate::print_phase;
14
15pub type ScopeContextLink = ComposerLink<ScopeContext>;
16#[derive(Clone)]
17pub struct ScopeContext {
18 pub scope: ScopeChain,
19 pub context: Rc<RefCell<GlobalContext>>
20}
21
22impl std::fmt::Debug for ScopeContext {
23 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
24 f.debug_struct("ScopeContext")
25 .field("scope", &self.scope)
26 .field("context", &self.context)
27 .finish()
28 }
29}
30
31impl std::fmt::Display for ScopeContext {
32 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
33 std::fmt::Debug::fmt(self, f)
34 }
35}
36
37impl ScopeContext {
38 pub fn print_with_message(&self, message: &str) {
39 print_phase!(message, "{}", self);
40 }
41 pub fn is_from_current_crate(&self) -> bool {
42 let context = self.context.borrow();
43 context.config.current_crate.ident().eq(self.scope.crate_ident_ref())
44 }
45 pub fn with(scope: ScopeChain, context: Rc<RefCell<GlobalContext>>) -> Self {
46 Self { scope, context }
47 }
48 pub fn cell_with(scope: ScopeChain, context: Rc<RefCell<GlobalContext>>) -> Rc<RefCell<Self>> {
49 Rc::new(RefCell::new(Self::with(scope, context)))
50 }
51 pub fn add_custom_conversion(&self, scope: ScopeChain, custom_type: Type, ffi_type: Type) {
52 let mut lock = self.context.borrow_mut();
54 lock.custom.add_conversion(custom_type, ObjectKind::unknown_type(ffi_type), scope);
55 }
56
57 pub fn maybe_custom_conversion(&self, ty: &Type) -> Option<Type> {
58 let lock = self.context.borrow();
59 lock.custom.maybe_type(ty)
60 }
61
62 pub fn maybe_fn_sig(&self, full_ty: &Type) -> Option<TypeBareFn> {
63 let scope_item = match full_ty {
64 Type::Path(TypePath { path, .. }) => self.maybe_scope_item_obj_first(path),
65 _ => None,
66 };
67 match scope_item {
68 Some(ScopeItemKind::Item(Item::Type(ItemType { ty, ..}), ..)) => match &*ty {
69 Type::BareFn(bare) => Some(bare.clone()),
70 _ => None
71 }
72 _ => None
73 }
74 }
75
76 pub fn maybe_lambda_args<SPEC>(&self, ty: &Type) -> Option<CommaPunctuated<SPEC::Name>>
77 where SPEC: Specification {
78 self.maybe_fn_sig(ty)
79 .and_then(|ty| MaybeLambdaArgs::<SPEC>::maybe_lambda_arg_names(&ty))
80 }
81
82 pub fn maybe_to_fn_type(&self) -> Option<Type> {
83 match &self.scope.parent_object() {
84 Some(ObjectKind::Type(ref ty_model_kind) | ObjectKind::Item(ref ty_model_kind, ..)) => {
85 self.scope.parent_scope().map(|parent_scope| {
86 let context = self.context.borrow();
87 context.maybe_scope_ref_obj_first(parent_scope.self_path_ref())
88 .and_then(|parent_obj_scope| context.maybe_object_ref_by_tree_key(ty_model_kind.as_type(), parent_obj_scope)
89 .and_then(ObjectKind::maybe_type))
90 .unwrap_or_else(|| parent_scope.to_type())
91 })
92 },
93 _ => None
94 }
95 }
96
97 pub fn maybe_to_trait_fn_type<SPEC>(&self) -> Option<Type>
98 where SPEC: Specification,
99 FFIFullDictionaryPath<SPEC>: ToType {
100 match &self.scope.parent_object() {
101 Some(ObjectKind::Type(ref ty_conversion) | ObjectKind::Item(ref ty_conversion, ..)) => {
102 let full_parent_ty: Type = Resolve::resolve(ty_conversion.as_type(), self);
103 Some(Resolve::<SpecialType<SPEC>>::maybe_resolve(&full_parent_ty, self)
104 .map(|special| special.to_type())
105 .unwrap_or_else(|| ty_conversion.maybe_trait_model()
106 .and_then(|model| model.as_type().maybe_trait_object(self)
107 .and_then(|oc| oc.maybe_type_model_kind_ref()
108 .map(TypeModelKind::to_type)))
109 .unwrap_or_else(|| ty_conversion.to_type())))
110 },
111 _ => None
112 }
113 }
114
115 pub fn maybe_parent_trait_or_regular_model_kind(&self) -> Option<TypeModelKind> {
116 self.scope
117 .parent_object()
118 .and_then(|parent_obj| parent_obj.maybe_fn_or_trait_or_same_kind(self))
119 }
120
121 pub fn maybe_special_or_regular_ffi_full_path<SPEC>(&self, ty: &Type) -> Option<FFIFullPath<SPEC>>
122 where SPEC: Specification,
123 FFIFullDictionaryPath<SPEC>: ToType {
124 self.maybe_special_ffi_full_path::<SPEC>(ty)
125 .or_else(|| self.maybe_ffi_full_path(ty))
126 }
127 fn maybe_special_ffi_full_path<SPEC>(&self, ty: &Type) -> Option<FFIFullPath<SPEC>>
128 where SPEC: Specification,
129 FFIFullDictionaryPath<SPEC>: ToType {
130 Resolve::<SpecialType<SPEC>>::maybe_resolve(ty, self)
131 .map(FFIFullPath::from)
132 }
133 pub fn maybe_ffi_full_path<SPEC>(&self, ty: &Type) -> Option<FFIFullPath<SPEC>>
134 where SPEC: Specification {
135 Resolve::<TypeModelKind>::resolve(ty, self)
136 .to_type()
137 .maybe_resolve(self)
138 }
139
140 pub fn maybe_scope_item_obj_first(&self, path: &Path) -> Option<ScopeItemKind> {
141 let lock = self.context.borrow();
142 lock.maybe_scope_item_ref_obj_first(path).cloned()
143 }
144 pub fn maybe_opaque_object<SPEC>(&self, ty: &Type) -> Option<Type>
145 where SPEC: Specification,
146 FFIFullDictionaryPath<SPEC>: ToType {
147 let resolve_opaque = |path: &Path| {
148 if path.is_void() {
149 Some(FFIFullDictionaryPath::<SPEC>::Void.to_type())
150 } else {
151 match self.maybe_scope_item_obj_first(path)
152 .or_else(|| self.maybe_scope_item_obj_first(&path.lifetimes_cleaned())) {
153 Some(item) =>
154 (!item.is_labeled_for_export() && !item.is_labeled_for_register()).then(|| item.scope().to_type()),
155 None =>
156 (!path.is_fermentable_dictionary_type() && !path.is_primitive()).then(|| ty.clone())
157 }
158 }
159 };
160 match ty {
161 Type::Path(TypePath { path, .. }) =>
162 resolve_opaque(path),
163 Type::TraitObject(TypeTraitObject { dyn_token, bounds, .. }) => match bounds.len() {
164 1 => bounds.first()
165 .and_then(MaybeTraitBound::maybe_trait_bound)
166 .and_then(|TraitBound { path, .. }| resolve_opaque(path))
167 .map(|ty| match &ty {
168 Type::ImplTrait(..) |
169 Type::TraitObject(..) => ty,
170 _ => parse_quote!(#dyn_token #ty),
171 }),
172 _ => None
173 },
174 _ => None
175 }
176 }
177
178 pub fn maybe_object_by_key(&self, ty: &Type) -> Option<ObjectKind> {
179 let lock = self.context.borrow();
180 let result = lock.maybe_object_ref_by_tree_key(ty, &self.scope).cloned();
181 result
182 }
183
184 pub fn maybe_object_ref_by_key_in_scope(&self, search_key: &ScopeSearchKey, scope: &ScopeChain) -> Option<ObjectKind> {
185 let lock = self.context.borrow();
186 let result = lock.scope_register.maybe_object_ref_by_key_in_scope(search_key.clone(), scope);
187 result.cloned()
188 }
189
190 pub fn maybe_object_ref_by_value(&self, search_key: &ScopeSearchKey) -> Option<ObjectKind> {
191 let lock = self.context.borrow();
192 let result = lock.scope_register.maybe_object_ref_by_value(search_key.clone());
193 result.cloned()
194 }
195
196 pub fn maybe_object_by_value(&self, ty: &Type) -> Option<ObjectKind> {
197 let lock = self.context.borrow();
198 let result = lock.maybe_object_ref_by_value(ty).cloned();
199 result
200 }
201 pub fn maybe_object_by_predicate_ref(&self, predicate: &ScopeSearch) -> Option<ObjectKind> {
202 match predicate {
203 ScopeSearch::KeyInScope(search_key, scope) =>
204 self.maybe_object_ref_by_key_in_scope(search_key, scope),
205 ScopeSearch::Value(search_key) =>
206 self.maybe_object_ref_by_value(search_key),
207 ScopeSearch::KeyInComposerScope(search_key) => {
208 self.maybe_object_ref_by_key_in_scope(search_key, &self.scope)
209 }
210 }
211
212 }
213
214 pub fn maybe_type_model_kind(&self, ty: &Type) -> Option<TypeModelKind> {
215 let lock = self.context.borrow();
216 lock.maybe_type_model_kind_ref_by_key(ty, &self.scope).cloned()
217 }
218
219 pub fn full_type_for(&self, ty: &Type) -> Type {
220 let lock = self.context.borrow();
221 let full_ty = lock.maybe_object_ref_by_tree_key(ty, &self.scope)
222 .and_then(ObjectKind::maybe_type)
223 .unwrap_or_else(|| ty.clone());
224 full_ty
225 }
226
227
228 pub fn scope_type_for_path(&self, path: &Path) -> Option<Type> {
229 let lock = self.context.borrow();
230 lock.scope_register.scope_key_type_for_path(path, &self.scope)
231 }
232
233 pub fn trait_items_from_attributes(&self, attrs: &[Attribute]) -> Depunctuated<(TraitModelPart1, ScopeChain)> {
234 extract_trait_names(attrs)
235 .iter()
236 .filter_map(|trait_path| self.maybe_trait_scope_pair(&trait_path.to_type()))
237 .collect()
238 }
239
240 pub fn maybe_trait_scope_pair(&self, trait_name: &Type) -> Option<(TraitModelPart1, ScopeChain)> {
241 let lock = self.context.borrow();
242 lock.maybe_trait_scope_pair(trait_name, &self.scope)
243 }
244
245 pub fn maybe_item_trait(&self, trait_path: &Path) -> Option<ItemTrait> {
246 let lock = self.context.borrow();
247 lock.maybe_item_trait(trait_path)
248 }
249}