use std::{
cell::RefCell,
collections::{BTreeSet, HashMap, HashSet},
rc::Rc,
};
use ferricel_types::{
extensions::{BuilderChainDecl, BuilderStep, ExtensionDecl, UsedExtension},
functions::RuntimeFunction,
};
use walrus::{FunctionId, LocalId};
use crate::schema::ProtoSchema;
pub struct CompilerEnv {
pub(crate) functions: HashMap<RuntimeFunction, FunctionId>,
}
impl CompilerEnv {
pub fn get(&self, func: RuntimeFunction) -> FunctionId {
*self
.functions
.get(&func)
.unwrap_or_else(|| panic!("Function {} missing in runtime module", func.name()))
}
}
pub struct CompilerContext {
pub local_vars: HashMap<String, LocalId>,
pub schema: Option<ProtoSchema>,
pub container: Option<String>,
pub logger: slog::Logger,
pub extensions: Rc<ExtensionRegistry>,
pub used_extensions: Rc<RefCell<BTreeSet<UsedExtension>>>,
}
impl CompilerContext {
pub fn new(
schema: Option<ProtoSchema>,
container: Option<String>,
logger: slog::Logger,
extensions: &BTreeSet<ExtensionDecl>,
builder_chains: &[BuilderChainDecl],
) -> Self {
Self {
local_vars: HashMap::new(),
schema,
container,
logger,
extensions: Rc::new(ExtensionRegistry::new(extensions, builder_chains)),
used_extensions: Rc::new(RefCell::new(BTreeSet::new())),
}
}
pub fn with_local(&self, name: String, local_id: LocalId) -> Self {
let mut local_vars = self.local_vars.clone();
local_vars.insert(name, local_id);
Self {
local_vars,
schema: self.schema.clone(),
container: self.container.clone(),
logger: self.logger.clone(),
extensions: self.extensions.clone(),
used_extensions: self.used_extensions.clone(),
}
}
pub fn record_extension(&self, namespace: Option<&str>, function: &str) {
self.used_extensions.borrow_mut().insert(UsedExtension {
namespace: namespace.map(|s| s.to_string()),
function: function.to_string(),
});
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ExtensionKey {
pub namespace: Option<String>,
pub function: String,
}
impl ExtensionKey {
pub fn new(namespace: Option<String>, function: String) -> Self {
Self {
namespace,
function,
}
}
}
pub struct ExtensionRegistry {
pub by_name: HashMap<ExtensionKey, ExtensionDecl>,
pub namespaces: HashSet<String>,
pub builder_entries: HashMap<String, BuilderStep>,
pub builder_steps: HashMap<String, Vec<BuilderStep>>,
}
impl ExtensionRegistry {
pub fn new(extensions: &BTreeSet<ExtensionDecl>, builder_chains: &[BuilderChainDecl]) -> Self {
let mut by_name = HashMap::new();
let mut namespaces = HashSet::new();
for decl in extensions {
if let Some(ref ns) = decl.namespace {
namespaces.insert(ns.clone());
}
by_name.insert(
ExtensionKey::new(decl.namespace.clone(), decl.function.clone()),
decl.clone(),
);
}
let mut builder_entries: HashMap<String, BuilderStep> = HashMap::new();
let mut builder_steps: HashMap<String, Vec<BuilderStep>> = HashMap::new();
for chain in builder_chains {
for step in &chain.steps {
match step {
BuilderStep::Entry { function, .. } => {
builder_entries.insert(function.clone(), step.clone());
}
BuilderStep::Chain { function, .. }
| BuilderStep::MapEntry { function, .. }
| BuilderStep::Terminal { function, .. } => {
builder_steps
.entry(function.clone())
.or_default()
.push(step.clone());
}
}
}
}
Self {
by_name,
namespaces,
builder_entries,
builder_steps,
}
}
pub fn is_empty(&self) -> bool {
self.by_name.is_empty() && self.builder_entries.is_empty()
}
}
pub enum CallShape<'a> {
Global,
Namespaced(String),
Receiver(Option<&'a cel::common::ast::IdedExpr>),
}