use std::sync::Arc;
use crate::inheritance::apply_substitution_to_conditional;
use crate::php_type::PhpType;
use crate::types::{ClassInfo, ELOQUENT_COLLECTION_FQN, MethodInfo, Visibility};
use crate::virtual_members::ResolvedClassCache;
use super::ELOQUENT_BUILDER_FQN;
pub(super) fn replace_eloquent_collection_typed(ty: &PhpType, custom_collection: &str) -> PhpType {
replace_collection_in_type(ty, custom_collection)
}
fn replace_collection_in_type(ty: &PhpType, custom_collection: &str) -> PhpType {
match ty {
PhpType::Generic(name, args) if name == ELOQUENT_COLLECTION_FQN => {
let new_args = args
.iter()
.map(|a| replace_collection_in_type(a, custom_collection))
.collect();
PhpType::Generic(custom_collection.to_string(), new_args)
}
PhpType::Generic(name, args) => {
let new_args = args
.iter()
.map(|a| replace_collection_in_type(a, custom_collection))
.collect();
PhpType::Generic(name.clone(), new_args)
}
PhpType::Union(members) => PhpType::Union(
members
.iter()
.map(|m| replace_collection_in_type(m, custom_collection))
.collect(),
),
PhpType::Intersection(members) => PhpType::Intersection(
members
.iter()
.map(|m| replace_collection_in_type(m, custom_collection))
.collect(),
),
PhpType::Nullable(inner) => PhpType::Nullable(Box::new(replace_collection_in_type(
inner,
custom_collection,
))),
PhpType::Array(inner) => PhpType::Array(Box::new(replace_collection_in_type(
inner,
custom_collection,
))),
other => other.clone(),
}
}
pub(super) fn build_builder_forwarded_methods(
class: &ClassInfo,
class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>,
_cache: Option<&ResolvedClassCache>,
) -> Vec<MethodInfo> {
let builder_class = match class_loader(ELOQUENT_BUILDER_FQN) {
Some(c) => c,
None => return Vec::new(),
};
let resolved_builder =
crate::virtual_members::resolve_class_fully(&builder_class, class_loader);
let builder_self_type = PhpType::Generic(
ELOQUENT_BUILDER_FQN.to_string(),
vec![PhpType::Named(class.name.clone())],
);
let mut subs = super::self_ref_subs(builder_self_type);
for param in &builder_class.template_params {
subs.insert(param.clone(), PhpType::Named(class.name.clone()));
}
let mut methods = Vec::new();
for method in &resolved_builder.methods {
if method.visibility != Visibility::Public {
continue;
}
if method.name.starts_with("__") {
continue;
}
if class
.methods
.iter()
.any(|m| m.name == method.name && m.is_static)
{
continue;
}
let mut forwarded = method.clone();
forwarded.is_static = true;
if !subs.is_empty() {
if let Some(ref mut ret) = forwarded.return_type {
*ret = ret.substitute(&subs);
}
if let Some(ref mut cond) = forwarded.conditional_return {
apply_substitution_to_conditional(cond, &subs);
}
for param in &mut forwarded.parameters {
if let Some(ref mut hint) = param.type_hint {
*hint = hint.substitute(&subs);
}
}
}
if let Some(coll) = class.laravel().and_then(|l| l.custom_collection.as_ref())
&& let Some(coll_name) = coll.base_name()
&& let Some(ref mut ret) = forwarded.return_type
{
*ret = replace_eloquent_collection_typed(ret, coll_name);
}
methods.push(forwarded);
}
methods
}
#[cfg(test)]
#[path = "builder_tests.rs"]
mod tests;