use serde::{Deserialize, Serialize};
use super::error::ExecuteError;
use super::tags::{
Container, DynamicPolicy, DynamicPolicyMonoInput, DynamicPolicyMonoResult, DynamicState,
};
use super::Result;
use crate::ast2::JsonPlusEntity;
use std::str::FromStr;
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone)]
pub enum InlineStatus {
#[default]
JustCreated,
Repeat,
Completed,
}
impl ToString for InlineStatus {
fn to_string(&self) -> String {
match self {
InlineStatus::JustCreated => "created".to_string(),
InlineStatus::Repeat => "repeat".to_string(),
InlineStatus::Completed => "completed".to_string(),
}
}
}
impl FromStr for InlineStatus {
type Err = ExecuteError;
fn from_str(s: &str) -> Result<Self> {
match s {
"created" => Ok(InlineStatus::JustCreated),
"repeat" => Ok(InlineStatus::Repeat),
"completed" => Ok(InlineStatus::Completed),
_ => Err(ExecuteError::UnsupportedStatus(s.to_string())),
}
}
}
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct InlineState {
pub status: InlineStatus,
}
pub struct InlinePolicy;
impl DynamicPolicy for InlinePolicy {
type State = InlineState;
fn mono(
inputs: DynamicPolicyMonoInput<Self::State>,
) -> Result<DynamicPolicyMonoResult<Self::State>> {
tracing::debug!("tag_inline::InlinePolicy::mono\nState = {:?}", inputs.state);
let (mut result, mut residual) =
DynamicPolicyMonoResult::<Self::State>::from_inputs(inputs);
match (residual.container, residual.state.status) {
(Container::Tag(_) | Container::BeginAnchor(_, _), InlineStatus::JustCreated) => {
if !residual.readonly {
let context_name = residual
.arguments
.arguments
.get(0)
.ok_or_else(|| ExecuteError::MissingInlineArgument {
range: residual.arguments.range,
})?
.value
.clone();
residual.state.status = InlineStatus::Completed;
result.new_state = Some(residual.state);
let context = residual.worker.read_context(&context_name)?;
let context = Self::slice_with_markers(
&context,
residual.parameters.get_as_integer_only("begin_line"),
residual
.parameters
.get_as_string_only("begin_marker")
.as_deref(),
residual.parameters.get_as_integer_only("end_line"),
residual
.parameters
.get_as_string_only("end_marker")
.as_deref(),
);
let context = match residual.parameters.get("data") {
Some(JsonPlusEntity::Object(data)) => {
residual.worker.process_context_with_data(context, data)?
}
Some(_) => {
return Err(ExecuteError::UnsupportedDataParameter {
range: residual.parameters.range,
});
}
None => context,
};
result.new_output = Some(context);
}
result.do_next_pass = true;
}
(Container::BeginAnchor(_, _), InlineStatus::Completed) => {
}
(Container::BeginAnchor(_, _), InlineStatus::Repeat) => {
if !residual.readonly {
residual.state.status = InlineStatus::JustCreated;
result.new_state = Some(residual.state);
result.new_output = Some(String::new());
}
result.do_next_pass = true;
}
_ => {}
}
Ok(result)
}
}
impl InlinePolicy {
fn slice_with_markers(
text: &str,
begin_line: Option<i64>,
begin_marker: Option<&str>,
end_line: Option<i64>,
end_marker: Option<&str>,
) -> String {
let lines: Vec<&str> = text.lines().collect();
let begin = if let Some(marker) = begin_marker {
let begin_marker_line = lines
.iter()
.position(|l| l.contains(marker))
.map(|idx| idx as i64)
.unwrap_or(0);
begin_marker_line + begin_line.unwrap_or(0)
} else {
begin_line.unwrap_or(1) - 1
};
let end = if let Some(marker) = end_marker {
let end_marker_line = lines
.iter()
.rposition(|l| l.contains(marker))
.map(|idx| idx as i64)
.unwrap_or(lines.len() as i64);
end_marker_line + end_line.unwrap_or(0)
} else {
end_line.unwrap_or(lines.len() as i64)
};
tracing::debug!(
"Slicing with markers: begin_line={:?}, begin_marker={:?}, end_line={:?}, end_marker={:?} => begin={}, end={}",
begin_line,
begin_marker,
end_line,
end_marker,
begin,
end
);
let begin = begin.clamp(0, lines.len() as i64) as usize;
let end = end.clamp(0, lines.len() as i64) as usize;
if begin >= end {
return String::new();
}
let mut joint = lines[begin..end].join("\n");
joint.push('\n');
joint
}
}
impl DynamicState for InlineState {
fn status_indicator(&self) -> String {
self.status.to_string()
}
}