use crate::{bif::Bif, constants::*, shared::Shared, utils::extract_blocks};
use std::rc::Rc;
pub(crate) struct BlockInherit {
pub(crate) indir: String,
pub(crate) last_bif_out: bool,
pub(crate) last_coalesce_out: bool,
pub(crate) block_count: u64, pub(crate) bif_count: u64, pub(crate) alias: String,
pub(crate) current_file: String,
pub(crate) current_dir: String,
pub(crate) include_files: Vec<String>,
pub(crate) locale_files: Vec<String>,
pub(crate) data_files: Vec<String>,
pub(crate) in_cache: bool,
pub(crate) in_only: bool,
}
impl Clone for BlockInherit {
fn clone(&self) -> Self {
BlockInherit {
indir: self.indir.clone(),
last_bif_out: self.last_bif_out,
last_coalesce_out: self.last_coalesce_out,
block_count: self.block_count,
bif_count: self.bif_count,
alias: self.alias.clone(),
current_file: self.current_file.clone(),
current_dir: self.current_dir.clone(),
include_files: self.include_files.clone(),
locale_files: self.locale_files.clone(),
data_files: self.data_files.clone(),
in_cache: self.in_cache,
in_only: self.in_only,
}
}
}
impl BlockInherit {
pub(crate) fn new() -> Self {
BlockInherit {
indir: "block_0".to_string(),
last_bif_out: false,
last_coalesce_out: false,
block_count: 0,
bif_count: 0,
alias: String::new(),
current_file: String::new(),
current_dir: String::new(),
include_files: Vec::new(),
locale_files: Vec::new(),
data_files: Vec::new(),
in_cache: false,
in_only: false,
}
}
pub(crate) fn create_block_schema(&mut self, shared: &mut Shared) -> String {
let prev_id = self.indir.clone();
let block_id = if self.block_count < 1 {
"block_1".to_string()
} else {
format!("block_{}", self.block_count)
};
if prev_id != block_id {
if let Some(parent_rc) = shared.indir_store.get(&prev_id) {
let parent_rc = Rc::clone(parent_rc);
shared.indir_store.insert(block_id.clone(), parent_rc);
}
}
self.indir = block_id.clone();
block_id
}
}
pub(crate) struct BlockParser<'a> {
shared: &'a mut Shared,
inherit: BlockInherit,
_none: &'a str,
}
impl Drop for BlockParser<'_> {
fn drop(&mut self) {
let block_id = format!("block_{}", self.inherit.block_count);
if block_id != "block_1" {
if block_id == self.inherit.indir {
self.shared.indir_store.remove(&block_id);
}
}
}
}
impl<'a> BlockParser<'a> {
pub(crate) fn new(shared: &'a mut Shared, mut inherit: BlockInherit) -> Self {
inherit.block_count += 1;
BlockParser {
shared,
inherit,
_none: "",
}
}
pub(crate) fn update_indir(&mut self, indir: &String) {
if let Some(child_rc) = self.shared.indir_store.get(&self.inherit.indir) {
let child_rc = Rc::clone(child_rc);
self.shared.indir_store.insert(indir.clone(), child_rc);
}
}
pub(crate) fn parse(&mut self, raw_source: &'a str, only: &str) -> String {
let blocks = match extract_blocks(raw_source) {
Ok(b) => b,
Err(p) => {
self.handle_unmatched_block(p);
return EMPTY_STRING;
}
};
self.parse_with_blocks(raw_source, &blocks, only)
}
fn handle_unmatched_block(&mut self, p: usize) {
self.shared.status_code = "500".to_string();
self.shared.status_param = format!("Unmatched block at position {}", p);
eprintln!("Unmatched block at position {}", p);
if let Some(text) = STATUS_CODES.get(self.shared.status_code.as_str()) {
self.shared.status_text = text.to_string();
} else {
self.shared.status_text = EMPTY_STRING;
}
}
pub(crate) fn parse_with_blocks(
&mut self,
raw_source: &'a str,
blocks: &[(usize, usize)],
only: &str,
) -> String {
let mut prev_end = 0;
let mut out = String::new();
for (start, end) in blocks {
let start = *start;
let end = *end;
let is_comment = raw_source[start..end].starts_with(BIF_COMMENT_OPEN);
let is_short_circuit_coalesce =
self.inherit.last_coalesce_out && self.inherit.alias == "coalesce";
if self.shared.exit {
return out.clone();
}
if prev_end < start {
out += &raw_source[prev_end..start];
}
if !is_comment && !is_short_circuit_coalesce {
let mut bif = Bif::new(
&raw_source[start..end],
self.shared,
&mut self.inherit,
only,
);
out += &bif.parse();
}
prev_end = end;
}
out += &raw_source[prev_end..];
out.trim().to_string()
}
}