use std::{borrow::Cow, str::FromStr, sync::atomic::AtomicPtr};
use heck::ToLowerCamelCase;
use itertools::Itertools;
use ript_config::RiptDotToml;
use rustc_driver::{Callbacks, Compilation};
use rustc_errors::DiagCtxtHandle;
use rustc_span::source_map::SourceMap;
use crate::{
analyzer::RiptAnalyzer,
codegen::{RiptCodegen, new_swc_emitter},
namespace::NamespacedPath,
swc_utils,
};
pub struct DefaultCallbacks;
impl Callbacks for DefaultCallbacks {}
pub struct KindInertiaTsCallbacks;
impl Callbacks for KindInertiaTsCallbacks {
fn after_analysis(
&mut self,
compiler: &rustc_interface::interface::Compiler,
tcx: rustc_middle::ty::TyCtxt<'_>,
) -> rustc_driver::Compilation {
let config_ = load_config_files(tcx.dcx(), compiler.sess.source_map());
CONFIG.store(
Box::into_raw(Box::new(config_)),
std::sync::atomic::Ordering::SeqCst,
);
let mut ra = RiptAnalyzer::new(tcx);
tcx.hir_walk_toplevel_module(&mut ra);
let mut w = match config().outfile_writer() {
Ok(w) => w,
Err(e) => {
tcx.dcx().err(format!("failed to open ts writer: {e}"));
return Compilation::Stop;
}
};
let comments = swc_utils::get_comments();
let swc_emitter = match new_swc_emitter().w(&mut w).comments(&comments).call() {
Ok(w) => w,
Err(e) => {
tcx.dcx().err(format!("failed to create swc emitter: {e}"));
return Compilation::Stop;
}
};
let cg = RiptCodegen::builder()
.analyzer(ra)
.tcx(tcx)
.swc_emitter(swc_emitter)
.build();
if let Err(e) = cg.emit_all() {
tcx.dcx().err(format!("failed to emit ts: {e}"));
};
Compilation::Stop
}
}
fn load_config_files(dcx: DiagCtxtHandle<'_>, source_map: &SourceMap) -> RiptDotToml {
let config_path = match RiptDotToml::provision() {
Ok(p) => p,
Err(e) => {
dcx.fatal(format!("failed to provision config file: {e}"));
}
};
let f = match source_map.load_file(&config_path) {
Ok(f) => f,
Err(e) => {
dcx.fatal(format!("failed to load config file: {e}"));
}
};
match RiptDotToml::from_str(f.src.as_deref().map(|f| f.as_str()).unwrap_or_default()) {
Ok(c) => c,
Err(e) => {
dcx.fatal(format!("failed to parse config file: {e}"));
}
}
}
static CONFIG: AtomicPtr<RiptDotToml> = AtomicPtr::new(std::ptr::null_mut());
pub fn config() -> &'static RiptDotToml {
unsafe { &*CONFIG.load(std::sync::atomic::Ordering::SeqCst) }
}
pub trait ConfigStrExt {
fn namespace_name(&self) -> Cow<'_, str>;
}
impl ConfigStrExt for str {
fn namespace_name(&self) -> Cow<'_, str> {
if config().camelize_namespaces() {
Cow::Owned(self.to_lower_camel_case())
} else {
Cow::Borrowed(self)
}
}
}
pub trait ConfigNamespacedPathExt {
fn override_for_namespaced_path(&self) -> Option<ript_config::TsKeywordType>;
}
impl ConfigNamespacedPathExt for NamespacedPath {
fn override_for_namespaced_path(&self) -> Option<ript_config::TsKeywordType> {
let overrides = config().overrides();
let self_as_key = self.clone().rev().join("::");
overrides.get(&self_as_key).copied()
}
}