use super::{DocumentationLanguage, SnippetRenderContext, parse_language};
use crate::core::config::{Language, ResolvedCrateConfig};
use crate::e2e::fixture::Fixture;
pub(super) fn function_excluded_for_language(
fixture: &Fixture,
language: &str,
generator_language_name: &str,
context: &SnippetRenderContext<'_>,
) -> bool {
let Some(DocumentationLanguage::Binding(lang)) = parse_language(generator_language_name) else {
return false;
};
let docs_fixture = fixture.docs_call_fixture();
let call = context.e2e.resolve_call_for_fixture(
docs_fixture.call.as_deref(),
&docs_fixture.id,
&docs_fixture.resolved_category(),
&docs_fixture.tags,
&docs_fixture.input,
);
let Some(function_name) = call.core_lookup_name(language) else {
return false;
};
let (excluded_functions, _) = crate::docs::language_pages::excludes::language_excludes(context.crate_config, lang);
excluded_functions.contains(function_name.as_ref())
}
pub(super) fn visitor_excluded_for_language(
fixture: &Fixture,
generator_language_name: &str,
context: &SnippetRenderContext<'_>,
) -> bool {
if fixture.visitor.is_none() {
return false;
}
let Some(DocumentationLanguage::Binding(lang)) = parse_language(generator_language_name) else {
return false;
};
let (excluded_functions, _) = crate::docs::language_pages::excludes::language_excludes(context.crate_config, lang);
excluded_functions.contains(crate::e2e::fixture::VISITOR_EXCLUDE_FUNCTION_NAME)
}
pub(super) fn function_binding_excluded_for_language(
fixture: &Fixture,
language: &str,
generator_language_name: &str,
context: &SnippetRenderContext<'_>,
) -> bool {
let Some(DocumentationLanguage::Binding(lang)) = parse_language(generator_language_name) else {
return false;
};
if lang == Language::Rust {
return false;
}
let docs_fixture = fixture.docs_call_fixture();
let call = context.e2e.resolve_call_for_fixture(
docs_fixture.call.as_deref(),
&docs_fixture.id,
&docs_fixture.resolved_category(),
&docs_fixture.tags,
&docs_fixture.input,
);
let Some(function_name) = call.core_lookup_name(language) else {
return false;
};
if let Some(function) = context.functions.iter().find(|function| function.name == function_name) {
return function.binding_excluded;
}
let mut methods = context
.type_defs
.iter()
.flat_map(|type_def| {
type_def
.methods
.iter()
.map(move |method| (type_def.name.as_str(), method))
})
.filter(|(_, method)| method.name == function_name);
let Some((first_type, first)) = methods.next() else {
return false;
};
if !methods.all(|(_, other)| other.binding_excluded == first.binding_excluded) {
return false;
}
if !first.binding_excluded {
return false;
}
!adapter_binds_method_for_language(context.crate_config, first_type, function_name.as_ref(), lang)
}
fn adapter_binds_method_for_language(
crate_config: &ResolvedCrateConfig,
type_name: &str,
method_name: &str,
lang: Language,
) -> bool {
let lang_name = lang.to_string();
crate_config.adapters.iter().any(|adapter| {
crate::core::config::extras::adapter_covers_method(adapter, type_name, method_name)
&& !adapter.skip_languages.contains(&lang_name)
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::config::{AdapterConfig, AdapterPattern};
fn streaming_adapter(owner_type: &str, core_path: &str, skip_languages: Vec<String>) -> AdapterConfig {
AdapterConfig {
name: "chat_stream".to_string(),
pattern: AdapterPattern::Streaming,
core_path: core_path.to_string(),
params: Vec::new(),
returns: None,
error_type: None,
owner_type: Some(owner_type.to_string()),
item_type: Some("ChatCompletionChunk".to_string()),
gil_release: false,
trait_name: None,
trait_method: None,
detect_async: false,
request_type: None,
skip_languages,
}
}
#[test]
fn adapter_binds_method_for_language_matches_a_fully_qualified_core_path() {
let config = ResolvedCrateConfig {
adapters: vec![streaming_adapter(
"DefaultClient",
"sample_stream_rs::DefaultClient::chat_stream",
Vec::new(),
)],
..ResolvedCrateConfig::default()
};
assert!(adapter_binds_method_for_language(
&config,
"DefaultClient",
"chat_stream",
Language::Python
));
}
#[test]
fn adapter_binds_method_for_language_matches_a_bare_core_path() {
let config = ResolvedCrateConfig {
adapters: vec![streaming_adapter("DefaultClient", "chat_stream", Vec::new())],
..ResolvedCrateConfig::default()
};
assert!(adapter_binds_method_for_language(
&config,
"DefaultClient",
"chat_stream",
Language::Python
));
}
#[test]
fn adapter_binds_method_for_language_does_not_match_a_different_owner() {
let config = ResolvedCrateConfig {
adapters: vec![streaming_adapter(
"DefaultClient",
"sample_stream_rs::DefaultClient::chat_stream",
Vec::new(),
)],
..ResolvedCrateConfig::default()
};
assert!(!adapter_binds_method_for_language(
&config,
"OtherClient",
"chat_stream",
Language::Python
));
}
#[test]
fn adapter_binds_method_for_language_respects_skip_languages() {
let config = ResolvedCrateConfig {
adapters: vec![streaming_adapter(
"DefaultClient",
"sample_stream_rs::DefaultClient::chat_stream",
vec!["python".to_string()],
)],
..ResolvedCrateConfig::default()
};
assert!(!adapter_binds_method_for_language(
&config,
"DefaultClient",
"chat_stream",
Language::Python
));
assert!(adapter_binds_method_for_language(
&config,
"DefaultClient",
"chat_stream",
Language::Node
));
}
}