use crate::generator::{
parser::{
command::resource_location::ResourceLocationRef, Line, ScheduleOperation, SelectorValue,
},
DebugDatapackMetadata,
};
use minect::command::named_logged_block_commands;
use std::collections::BTreeMap;
pub struct TemplateEngine<'l> {
replacements: BTreeMap<&'l str, &'l str>,
replacements_owned: BTreeMap<&'l str, String>,
adapter_listener_name: &'l str,
}
impl<'l> TemplateEngine<'l> {
pub fn new(
replacements: BTreeMap<&'l str, &'l str>,
adapter_listener_name: &'l str,
) -> TemplateEngine<'l> {
TemplateEngine {
replacements,
replacements_owned: BTreeMap::new(),
adapter_listener_name,
}
}
pub fn extend<T: IntoIterator<Item = (&'l str, &'l str)>>(
&self,
iter: T,
) -> TemplateEngine<'l> {
let mut replacements = self.replacements.clone();
replacements.extend(iter);
TemplateEngine {
replacements,
replacements_owned: self.replacements_owned.clone(),
adapter_listener_name: self.adapter_listener_name,
}
}
pub fn extend_orig_name<N: AsRef<str>>(
&'l self,
orig_name: &'l ResourceLocationRef<N>,
) -> TemplateEngine<'l> {
let mut engine = self.extend([
("-orig_ns-", orig_name.namespace()),
("-orig/fn-", orig_name.path()),
]);
let orig_fn_tag = orig_name.path().replace('/', "+");
engine.replacements_owned.insert("-orig+fn-", orig_fn_tag);
engine
}
pub fn expand(&self, string: &str) -> String {
let mut with_replacements_applied = string.to_owned();
for (from, to) in &self.replacements {
with_replacements_applied = with_replacements_applied.replace(from, to);
}
for (from, to) in &self.replacements_owned {
with_replacements_applied = with_replacements_applied.replace(from, to);
}
let mut result = String::new();
let mut lines = with_replacements_applied.split_inclusive('\n');
while let Some(line) = lines.next() {
match line.trim() {
"# -minect_log-" => {
if let Some(command) = lines.next() {
let commands =
named_logged_block_commands(self.adapter_listener_name, command.trim());
result.push_str(&commands.join("\n"));
if command.ends_with('\n') {
result.push('\n');
}
}
}
"# -minect_log_conditional-" => {
if let Some(command) = lines.next() {
let command_trimmed = command.trim();
if let Some(index) = command_trimmed.find(" run ") {
let execute = &command_trimmed[..index + 5];
let inner_command = &command_trimmed[index + 5..];
let commands = named_logged_block_commands(
self.adapter_listener_name,
inner_command,
);
result.push_str(execute);
result.push_str(&commands[0]);
result.push('\n');
result.push_str(execute);
result.push_str(&commands[1]);
if command.ends_with('\n') {
result.push('\n');
}
}
}
}
_ => {
result.push_str(line);
}
}
}
result
}
pub fn expand_line(
&self,
(_line_number, line, command): &(usize, String, Line),
metadata: &DebugDatapackMetadata,
) -> String {
match command {
Line::FunctionCall { .. } => {
unreachable!()
}
Line::OptionalSelectorCommand {
missing_selector,
selectors,
..
} => {
const SELF_SELECTOR: &str = " @s";
let mut line = line.to_string();
line.insert_str(*missing_selector, SELF_SELECTOR);
let mut selectors = selectors
.iter()
.map(|(index, selector)| {
let mut index = *index;
if index >= *missing_selector {
index += SELF_SELECTOR.len();
}
(index, selector.clone())
})
.collect::<BTreeMap<_, _>>();
selectors.insert(*missing_selector + 1, SelectorValue::empty_self());
let line = exclude_internal_entites_from_selectors(&line, &selectors);
self.expand(&line)
}
Line::Schedule {
schedule_start,
function,
operation,
selectors,
..
} => {
let schedule_fn = function.path();
let schedule_plus_fn = function.path().replace('/', "+");
let fn_score_holder = metadata.get_fn_score_holder(function);
let execute =
exclude_internal_entites_from_selectors(&line[..*schedule_start], selectors);
let mut engine = self.extend([
("-schedule_ns-", function.namespace()),
("-schedule/fn-", &schedule_fn),
("-schedule+fn-", &schedule_plus_fn),
("-fn_score_holder-", &fn_score_holder),
("execute run ", &execute),
]);
let ticks;
if let ScheduleOperation::Append { time } | ScheduleOperation::Replace { time } =
operation
{
ticks = time.as_ticks().to_string();
engine = engine.extend([("-ticks-", ticks.as_str())]);
}
let template = match operation {
ScheduleOperation::Append { .. } => {
include_template!("data/template/functions/schedule_append.mcfunction")
}
ScheduleOperation::Clear => {
include_template!("data/template/functions/schedule_clear.mcfunction")
}
ScheduleOperation::Replace { .. } => {
include_template!("data/template/functions/schedule_replace.mcfunction")
}
};
engine.expand(template)
}
Line::OtherCommand { selectors, .. } => {
let line = exclude_internal_entites_from_selectors(line, selectors);
self.expand(&line)
}
Line::Comment => self.expand(&line),
Line::Empty => line.to_owned(),
}
}
}
pub fn exclude_internal_entites_from_selectors(
line: &str,
selectors: &BTreeMap<usize, SelectorValue>,
) -> String {
let mut index = 0;
let mut result = String::new();
for (selector_index, selector) in selectors {
const MIN_SELECTOR_LEN: usize = "@e".len();
let (prefix, remaining_line) = line.split_at(selector_index + MIN_SELECTOR_LEN);
result.push_str(&prefix[index..]);
index = prefix.len();
result.push_str("[tag=!-ns-");
if selector.exclude_minect_cursor {
result.push_str(",tag=!minect_cursor");
}
if remaining_line.starts_with('[') {
index += 1;
result.push(',');
} else {
result.push(']');
}
}
result.push_str(&line[index..]);
result
}