use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};
use crate::dsl::ast::Statement;
use crate::kernel::PolydatProgram;
use super::error::SourceContext;
use super::pull::RegisteredPullConsumer;
use super::spec::{ExportSpec, ImportSpec};
#[derive(Debug, Clone)]
pub enum BodyFragment {
PolydatSource(String),
Statements(Vec<Statement>),
}
pub struct ScopeContract<M> {
imports: Vec<ImportHandle<M>>,
exports: Vec<ExportHandle<M>>,
_module: PhantomData<fn() -> M>,
}
impl<M> ScopeContract<M> {
pub(crate) fn from_specs(imports: &[ImportSpec], exports: &[ExportSpec]) -> Self {
Self {
imports: imports
.iter()
.map(|s| ImportHandle {
name: s.name.clone(),
_module: PhantomData,
})
.collect(),
exports: exports
.iter()
.map(|s| ExportHandle {
name: s.name.clone(),
_module: PhantomData,
})
.collect(),
_module: PhantomData,
}
}
pub fn imports(&self) -> &[ImportHandle<M>] {
&self.imports
}
pub fn exports(&self) -> &[ExportHandle<M>] {
&self.exports
}
}
impl<M> std::fmt::Debug for ScopeContract<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ScopeContract")
.field("imports", &self.imports)
.field("exports", &self.exports)
.finish()
}
}
pub struct ImportHandle<M> {
name: String,
_module: PhantomData<fn() -> M>,
}
impl<M> ImportHandle<M> {
pub fn name(&self) -> &str {
&self.name
}
}
impl<M> std::fmt::Debug for ImportHandle<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ImportHandle").field(&self.name).finish()
}
}
pub struct ExportHandle<M> {
name: String,
_module: PhantomData<fn() -> M>,
}
impl<M> ExportHandle<M> {
pub fn name(&self) -> &str {
&self.name
}
}
impl<M> std::fmt::Debug for ExportHandle<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ExportHandle").field(&self.name).finish()
}
}
#[derive(Debug, Clone)]
pub struct WriteThroughBinding {
pub export_name: String,
pub source_output: String,
}
pub struct ScopeModule<M> {
pub(crate) imports: Vec<ImportSpec>,
pub(crate) exports: Vec<ExportSpec>,
pub(crate) program: Arc<PolydatProgram>,
pub(crate) statements: Vec<Statement>,
pub(crate) options: crate::dsl::compile::CompileOptions,
pub(crate) inherited_outputs: Vec<String>,
pub(crate) programs: Mutex<HashMap<crate::Engine, Arc<dyn crate::kernel::KernelProgram>>>,
pub(crate) contract: ScopeContract<M>,
pub(crate) context: SourceContext,
pub(crate) consumers: Vec<RegisteredPullConsumer>,
pub(crate) write_throughs: Vec<WriteThroughBinding>,
pub(crate) diagnostics: Vec<String>,
pub(crate) _module: PhantomData<fn() -> M>,
}
impl<M> ScopeModule<M> {
pub fn program_on(
&self,
engine: crate::Engine,
) -> Result<Arc<dyn crate::kernel::KernelProgram>, crate::KernelError> {
let mut programs = self
.programs
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
if let Some(program) = programs.get(&engine) {
return Ok(program.clone());
}
let mut kernel = crate::dsl::compile::compile_template_with_engine(
&crate::dsl::ast::PolydatFile {
statements: self.statements.clone(),
},
&self.options,
engine,
)?;
if !self.inherited_outputs.is_empty() {
crate::kernel::KernelInternals::set_inherited_outputs(
kernel.as_mut(),
self.inherited_outputs.clone(),
);
}
let program = kernel.into_program();
programs.insert(engine, program.clone());
Ok(program)
}
pub fn instantiate_under(
&self,
parent: &dyn crate::kernel::Kernel,
engine: crate::Engine,
iter_bindings: &[(String, crate::ast::Value)],
) -> Result<Box<dyn crate::kernel::Kernel>, crate::KernelError> {
let program = self.program_on(engine)?;
let mut child = crate::kernel::bind_under(parent, program, iter_bindings)?;
if !self.write_throughs.is_empty() {
child.set_write_throughs(
self.write_throughs
.iter()
.map(|wt| (wt.export_name.clone(), wt.source_output.clone()))
.collect(),
);
}
Ok(child)
}
pub fn imports(&self) -> &[ImportSpec] {
&self.imports
}
pub fn exports(&self) -> &[ExportSpec] {
&self.exports
}
pub fn program(&self) -> &Arc<PolydatProgram> {
&self.program
}
pub fn contract(&self) -> &ScopeContract<M> {
&self.contract
}
pub fn context(&self) -> &SourceContext {
&self.context
}
pub fn consumers(&self) -> &[RegisteredPullConsumer] {
&self.consumers
}
pub fn diagnostics(&self) -> &[String] {
&self.diagnostics
}
pub fn write_throughs(&self) -> &[WriteThroughBinding] {
&self.write_throughs
}
}
impl<M> std::fmt::Debug for ScopeModule<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ScopeModule")
.field("imports", &self.imports)
.field("exports", &self.exports)
.field("context", &self.context)
.field("consumer_count", &self.consumers.len())
.field("write_throughs", &self.write_throughs)
.field("diagnostics", &self.diagnostics)
.finish()
}
}