ferment_sys/context/
scope_context.rs

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        // Here we don't know about types in pass 1, we can only use imports
53        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}