use std::any::TypeId;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use tree_sitter::Parser;
use crate::kit::{AsyncAutoBuilder, AsyncKit, ModuleMeta};
use crate::model::Language;
use super::capability::{ExtractorRegistry, ParserRegistry};
use super::dispatcher::get_extractor;
use super::error::ParseError;
use super::extractor::Extractor;
use super::parser_factory::ParserFactory;
pub struct ParserFactoryModule;
impl ModuleMeta for ParserFactoryModule {
const NAME: &'static str = "parser";
fn dependencies() -> &'static [(&'static str, TypeId)] {
&[]
}
}
impl AsyncAutoBuilder for ParserFactoryModule {
type Capability = Arc<dyn ParserRegistry>;
type Error = ParseError;
fn build<'a>(
_kit: &'a AsyncKit,
) -> Pin<Box<dyn Future<Output = Result<Self::Capability, Self::Error>> + Send + 'a>> {
Box::pin(async move { Self::build_cap() })
}
}
impl ParserFactoryModule {
pub(crate) fn build_cap() -> Result<Arc<dyn ParserRegistry>, ParseError> {
Ok(Arc::new(ParserRegistryCapability))
}
}
struct ParserRegistryCapability;
impl ParserRegistry for ParserRegistryCapability {
fn create_parser(&self, lang: Language) -> Result<Parser, ParseError> {
ParserFactory::create_parser(lang)
}
fn supported_languages(&self) -> Vec<Language> {
Language::compiled()
}
}
pub struct ExtractorRegistryModule;
impl ModuleMeta for ExtractorRegistryModule {
const NAME: &'static str = "extractor";
fn dependencies() -> &'static [(&'static str, TypeId)] {
&[]
}
}
impl AsyncAutoBuilder for ExtractorRegistryModule {
type Capability = Arc<dyn ExtractorRegistry>;
type Error = ParseError;
fn build<'a>(
_kit: &'a AsyncKit,
) -> Pin<Box<dyn Future<Output = Result<Self::Capability, Self::Error>> + Send + 'a>> {
Box::pin(async move { Self::build_cap() })
}
}
impl ExtractorRegistryModule {
pub(crate) fn build_cap() -> Result<Arc<dyn ExtractorRegistry>, ParseError> {
Ok(Arc::new(ExtractorRegistryCapability))
}
}
struct ExtractorRegistryCapability;
impl ExtractorRegistry for ExtractorRegistryCapability {
fn get_extractor(&self, language: Language) -> Box<dyn Extractor> {
get_extractor(language)
}
fn supported_languages(&self) -> Vec<Language> {
Language::compiled()
}
}
#[cfg(test)]
mod tests {
use crate::kit::{AsyncKit, ExtractorRegistryModule, ParserFactoryModule};
#[cfg(any(
feature = "lang-c",
feature = "lang-rust",
feature = "lang-fortran",
feature = "lang-python",
feature = "lang-typescript",
feature = "lang-go",
feature = "lang-java",
feature = "lang-cpp",
feature = "lang-javascript",
feature = "lang-ruby",
feature = "lang-haskell",
feature = "lang-ocaml",
feature = "lang-scala",
feature = "lang-php",
feature = "lang-csharp",
feature = "lang-bash",
feature = "lang-html",
feature = "lang-css",
feature = "lang-json",
feature = "lang-regex",
feature = "lang-verilog",
))]
#[test]
fn build_returns_capability() {
let cap = ParserFactoryModule::build_cap().expect("build_cap");
let langs = cap.supported_languages();
assert!(
!langs.is_empty(),
"at least one language must be compiled in"
);
}
#[test]
fn capability_creates_parser_for_compiled_languages() {
let cap = ParserFactoryModule::build_cap().expect("build_cap");
for lang in cap.supported_languages() {
let parser = cap.create_parser(lang);
assert!(parser.is_ok(), "create_parser failed for {lang}");
}
}
#[cfg(any(
feature = "lang-c",
feature = "lang-rust",
feature = "lang-fortran",
feature = "lang-python",
feature = "lang-typescript",
feature = "lang-go",
feature = "lang-java",
feature = "lang-cpp",
feature = "lang-javascript",
feature = "lang-ruby",
feature = "lang-haskell",
feature = "lang-ocaml",
feature = "lang-scala",
feature = "lang-php",
feature = "lang-csharp",
feature = "lang-bash",
feature = "lang-html",
feature = "lang-css",
feature = "lang-json",
feature = "lang-regex",
feature = "lang-verilog",
))]
#[tokio::test]
async fn kit_registration_flow() {
let mut kit = AsyncKit::new();
kit.register::<ParserFactoryModule>()
.expect("register::<ParserFactoryModule>");
let kit = kit.build().await.expect("build");
assert!(kit.contains::<ParserFactoryModule>());
let required = kit
.require::<ParserFactoryModule>()
.expect("require::<ParserFactoryModule>");
assert!(!required.supported_languages().is_empty());
}
#[cfg(any(
feature = "lang-c",
feature = "lang-rust",
feature = "lang-fortran",
feature = "lang-python",
feature = "lang-typescript",
feature = "lang-go",
feature = "lang-java",
feature = "lang-cpp",
feature = "lang-javascript",
feature = "lang-ruby",
feature = "lang-haskell",
feature = "lang-ocaml",
feature = "lang-scala",
feature = "lang-php",
feature = "lang-csharp",
feature = "lang-bash",
feature = "lang-html",
feature = "lang-css",
feature = "lang-json",
feature = "lang-regex",
feature = "lang-verilog",
))]
#[test]
fn extractor_build_returns_capability() {
let cap = ExtractorRegistryModule::build_cap().expect("build_cap");
let langs = cap.supported_languages();
assert!(
!langs.is_empty(),
"at least one language must be compiled in"
);
}
#[test]
fn extractor_capability_returns_extractor_for_compiled_languages() {
let cap = ExtractorRegistryModule::build_cap().expect("build_cap");
for lang in cap.supported_languages() {
let ext = cap.get_extractor(lang);
assert_eq!(
ext.language(),
lang,
"extractor should report its language as {lang}"
);
}
}
#[cfg(any(
feature = "lang-c",
feature = "lang-rust",
feature = "lang-fortran",
feature = "lang-python",
feature = "lang-typescript",
feature = "lang-go",
feature = "lang-java",
feature = "lang-cpp",
feature = "lang-javascript",
feature = "lang-ruby",
feature = "lang-haskell",
feature = "lang-ocaml",
feature = "lang-scala",
feature = "lang-php",
feature = "lang-csharp",
feature = "lang-bash",
feature = "lang-html",
feature = "lang-css",
feature = "lang-json",
feature = "lang-regex",
feature = "lang-verilog",
))]
#[tokio::test]
async fn extractor_kit_registration_flow() {
let mut kit = AsyncKit::new();
kit.register::<ExtractorRegistryModule>()
.expect("register::<ExtractorRegistryModule>");
let kit = kit.build().await.expect("build");
assert!(kit.contains::<ExtractorRegistryModule>());
let required = kit
.require::<ExtractorRegistryModule>()
.expect("require::<ExtractorRegistryModule>");
assert!(!required.supported_languages().is_empty());
}
}