#[cfg(test)]
mod tests;
use crate::encode::{Encode, EncodeState};
use crate::expr::Expression;
use crate::util::{dedent_by, min_leading_whitespace};
use crate::{parser, Decor, Decorate, Decorated, Ident, RawString, Spanned};
use std::fmt;
use std::ops::{Deref, DerefMut, Range};
use std::str::FromStr;
#[doc(inline)]
pub use hcl_primitives::template::Strip;
pub type IntoIter = Box<dyn Iterator<Item = Element>>;
pub type Iter<'a> = Box<dyn Iterator<Item = &'a Element> + 'a>;
pub type IterMut<'a> = Box<dyn Iterator<Item = &'a mut Element> + 'a>;
#[derive(Debug, Clone, Eq, Default)]
pub struct StringTemplate {
template: Template,
decor: Decor,
}
impl StringTemplate {
#[inline]
pub fn new() -> Self {
StringTemplate::default()
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
StringTemplate {
template: Template::with_capacity(capacity),
..Default::default()
}
}
pub(crate) fn despan(&mut self, input: &str) {
self.decor.despan(input);
self.template.despan(input);
}
}
impl From<Vec<Element>> for StringTemplate {
fn from(elements: Vec<Element>) -> Self {
StringTemplate {
template: Template::from(elements),
decor: Decor::default(),
}
}
}
impl PartialEq for StringTemplate {
fn eq(&self, other: &Self) -> bool {
self.template == other.template
}
}
impl Deref for StringTemplate {
type Target = Template;
#[inline]
fn deref(&self) -> &Self::Target {
&self.template
}
}
impl DerefMut for StringTemplate {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.template
}
}
impl<T> Extend<T> for StringTemplate
where
T: Into<Element>,
{
fn extend<I>(&mut self, iterable: I)
where
I: IntoIterator<Item = T>,
{
self.template.extend(iterable);
}
}
impl<T> FromIterator<T> for StringTemplate
where
T: Into<Element>,
{
fn from_iter<I>(iterable: I) -> Self
where
I: IntoIterator<Item = T>,
{
Template::from_iter(iterable).into()
}
}
impl IntoIterator for StringTemplate {
type Item = Element;
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.template.into_iter()
}
}
impl<'a> IntoIterator for &'a StringTemplate {
type Item = &'a Element;
type IntoIter = Iter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.template.iter()
}
}
impl<'a> IntoIterator for &'a mut StringTemplate {
type Item = &'a mut Element;
type IntoIter = IterMut<'a>;
fn into_iter(self) -> Self::IntoIter {
self.template.iter_mut()
}
}
#[derive(Debug, Clone, Eq)]
pub struct HeredocTemplate {
pub delimiter: Ident,
pub template: Template,
indent: Option<usize>,
trailing: RawString,
decor: Decor,
span: Option<Range<usize>>,
}
impl HeredocTemplate {
pub fn new(delimiter: Ident, template: Template) -> HeredocTemplate {
HeredocTemplate {
delimiter,
template,
indent: None,
trailing: RawString::default(),
decor: Decor::default(),
span: None,
}
}
pub fn indent(&self) -> Option<usize> {
self.indent
}
pub fn set_indent(&mut self, indent: usize) {
self.indent = Some(indent);
}
pub fn trailing(&self) -> &RawString {
&self.trailing
}
pub fn set_trailing(&mut self, trailing: impl Into<RawString>) {
self.trailing = trailing.into();
}
pub fn dedent(&mut self) {
let stripped_indent = self.template.dedent();
self.indent = stripped_indent;
}
pub(crate) fn despan(&mut self, input: &str) {
self.decor.despan(input);
self.template.despan(input);
self.trailing.despan(input);
}
}
impl PartialEq for HeredocTemplate {
fn eq(&self, other: &Self) -> bool {
self.delimiter == other.delimiter
&& self.template == other.template
&& self.indent == other.indent
&& self.trailing == other.trailing
}
}
#[derive(Debug, Clone, Eq, Default)]
pub struct Template {
elements: Vec<Element>,
span: Option<Range<usize>>,
}
impl Template {
#[inline]
pub fn new() -> Self {
Template::default()
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Template {
elements: Vec::with_capacity(capacity),
..Default::default()
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.elements.is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.elements.len()
}
#[inline]
pub fn clear(&mut self) {
self.elements.clear();
}
#[inline]
pub fn get(&self, index: usize) -> Option<&Element> {
self.elements.get(index)
}
#[inline]
pub fn get_mut(&mut self, index: usize) -> Option<&mut Element> {
self.elements.get_mut(index)
}
#[inline]
pub fn insert(&mut self, index: usize, element: impl Into<Element>) {
self.elements.insert(index, element.into());
}
#[inline]
pub fn push(&mut self, element: impl Into<Element>) {
self.elements.push(element.into());
}
#[inline]
pub fn pop(&mut self) -> Option<Element> {
self.elements.pop()
}
#[inline]
pub fn remove(&mut self, index: usize) -> Element {
self.elements.remove(index)
}
#[inline]
pub fn iter(&self) -> Iter<'_> {
Box::new(self.elements.iter())
}
#[inline]
pub fn iter_mut(&mut self) -> IterMut<'_> {
Box::new(self.elements.iter_mut())
}
pub fn as_single_element(&self) -> Option<&Element> {
match self.len() {
1 => self.get(0),
_ => None,
}
}
pub fn as_single_element_mut(&mut self) -> Option<&mut Element> {
match self.len() {
1 => self.get_mut(0),
_ => None,
}
}
pub(crate) fn despan(&mut self, input: &str) {
for element in &mut self.elements {
element.despan(input);
}
}
pub(crate) fn dedent(&mut self) -> Option<usize> {
let mut indent: Option<usize> = None;
let mut skip_first_line = false;
for element in &self.elements {
if let Element::Literal(literal) = element {
if let Some(leading_ws) = min_leading_whitespace(literal, skip_first_line) {
indent = Some(indent.map_or(leading_ws, |indent| indent.min(leading_ws)));
}
skip_first_line = !literal.ends_with('\n');
} else if !skip_first_line {
return None;
}
}
if let Some(indent) = indent {
skip_first_line = false;
for element in &mut self.elements {
if let Element::Literal(literal) = element {
let dedented = dedent_by(literal, indent, skip_first_line);
*literal.as_mut() = dedented.into_owned();
skip_first_line = !literal.ends_with('\n');
} else if !skip_first_line {
skip_first_line = true;
}
}
}
indent
}
}
impl PartialEq for Template {
fn eq(&self, other: &Self) -> bool {
self.elements == other.elements
}
}
impl fmt::Display for Template {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut state = EncodeState::new(f);
self.encode(&mut state)
}
}
impl FromStr for Template {
type Err = parser::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parser::parse_template(s)
}
}
impl From<Vec<Element>> for Template {
fn from(elements: Vec<Element>) -> Self {
Template {
elements,
..Default::default()
}
}
}
impl From<Template> for StringTemplate {
fn from(template: Template) -> Self {
StringTemplate {
template,
..Default::default()
}
}
}
impl From<StringTemplate> for Template {
fn from(template: StringTemplate) -> Self {
template.template
}
}
impl<T> Extend<T> for Template
where
T: Into<Element>,
{
fn extend<I>(&mut self, iterable: I)
where
I: IntoIterator<Item = T>,
{
let iter = iterable.into_iter();
let reserve = if self.is_empty() {
iter.size_hint().0
} else {
(iter.size_hint().0 + 1) / 2
};
self.elements.reserve(reserve);
iter.for_each(|v| self.push(v));
}
}
impl<T> FromIterator<T> for Template
where
T: Into<Element>,
{
fn from_iter<I>(iterable: I) -> Self
where
I: IntoIterator<Item = T>,
{
let iter = iterable.into_iter();
let lower = iter.size_hint().0;
let mut template = Template::with_capacity(lower);
template.extend(iter);
template
}
}
impl IntoIterator for Template {
type Item = Element;
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter {
Box::new(self.elements.into_iter())
}
}
impl<'a> IntoIterator for &'a Template {
type Item = &'a Element;
type IntoIter = Iter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> IntoIterator for &'a mut Template {
type Item = &'a mut Element;
type IntoIter = IterMut<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Element {
Literal(Spanned<String>),
Interpolation(Interpolation),
Directive(Box<Directive>),
}
impl Element {
pub fn is_literal(&self) -> bool {
self.as_literal().is_some()
}
pub fn as_literal(&self) -> Option<&Spanned<String>> {
match self {
Element::Literal(value) => Some(value),
Element::Interpolation(_) | Element::Directive(_) => None,
}
}
pub fn is_interpolation(&self) -> bool {
self.as_interpolation().is_some()
}
pub fn as_interpolation(&self) -> Option<&Interpolation> {
match self {
Element::Interpolation(value) => Some(value),
Element::Literal(_) | Element::Directive(_) => None,
}
}
pub fn is_directive(&self) -> bool {
self.as_directive().is_some()
}
pub fn as_directive(&self) -> Option<&Directive> {
match self {
Element::Directive(value) => Some(value),
Element::Literal(_) | Element::Interpolation(_) => None,
}
}
pub(crate) fn despan(&mut self, input: &str) {
match self {
Element::Literal(_) => {}
Element::Interpolation(interp) => interp.despan(input),
Element::Directive(dir) => dir.despan(input),
}
}
}
impl From<&str> for Element {
fn from(value: &str) -> Self {
Element::from(value.to_string())
}
}
impl From<String> for Element {
fn from(value: String) -> Self {
Element::from(Spanned::new(value))
}
}
impl From<Spanned<String>> for Element {
fn from(value: Spanned<String>) -> Self {
Element::Literal(value)
}
}
impl From<Interpolation> for Element {
fn from(value: Interpolation) -> Self {
Element::Interpolation(value)
}
}
impl From<Directive> for Element {
fn from(value: Directive) -> Self {
Element::Directive(Box::new(value))
}
}
#[derive(Debug, Clone, Eq)]
pub struct Interpolation {
pub expr: Expression,
pub strip: Strip,
span: Option<Range<usize>>,
}
impl Interpolation {
pub fn new(expr: impl Into<Expression>) -> Interpolation {
Interpolation {
expr: expr.into(),
strip: Strip::default(),
span: None,
}
}
pub(crate) fn despan(&mut self, input: &str) {
self.expr.despan(input);
}
}
impl PartialEq for Interpolation {
fn eq(&self, other: &Self) -> bool {
self.expr == other.expr && self.strip == other.strip
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Directive {
If(IfDirective),
For(ForDirective),
}
impl Directive {
pub(crate) fn despan(&mut self, input: &str) {
match self {
Directive::If(dir) => dir.despan(input),
Directive::For(dir) => dir.despan(input),
}
}
}
impl From<IfDirective> for Directive {
fn from(value: IfDirective) -> Self {
Directive::If(value)
}
}
impl From<ForDirective> for Directive {
fn from(value: ForDirective) -> Self {
Directive::For(value)
}
}
#[derive(Debug, Clone, Eq)]
pub struct IfDirective {
pub if_expr: IfTemplateExpr,
pub else_expr: Option<ElseTemplateExpr>,
pub endif_expr: EndifTemplateExpr,
span: Option<Range<usize>>,
}
impl IfDirective {
pub fn new(
if_expr: IfTemplateExpr,
else_expr: Option<ElseTemplateExpr>,
endif_expr: EndifTemplateExpr,
) -> IfDirective {
IfDirective {
if_expr,
else_expr,
endif_expr,
span: None,
}
}
pub(crate) fn despan(&mut self, input: &str) {
self.if_expr.despan(input);
if let Some(else_expr) = &mut self.else_expr {
else_expr.despan(input);
}
self.endif_expr.despan(input);
}
}
impl PartialEq for IfDirective {
fn eq(&self, other: &Self) -> bool {
self.if_expr == other.if_expr
&& self.else_expr == other.else_expr
&& self.endif_expr == other.endif_expr
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IfTemplateExpr {
pub cond_expr: Expression,
pub template: Template,
pub strip: Strip,
preamble: RawString,
}
impl IfTemplateExpr {
pub fn new(cond_expr: impl Into<Expression>, template: Template) -> IfTemplateExpr {
IfTemplateExpr {
preamble: RawString::default(),
cond_expr: cond_expr.into(),
template,
strip: Strip::default(),
}
}
pub fn preamble(&self) -> &RawString {
&self.preamble
}
pub fn set_preamble(&mut self, preamble: impl Into<RawString>) {
self.preamble = preamble.into();
}
pub(crate) fn despan(&mut self, input: &str) {
self.preamble.despan(input);
self.cond_expr.despan(input);
self.template.despan(input);
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ElseTemplateExpr {
pub template: Template,
pub strip: Strip,
preamble: RawString,
trailing: RawString,
}
impl ElseTemplateExpr {
pub fn new(template: Template) -> ElseTemplateExpr {
ElseTemplateExpr {
preamble: RawString::default(),
trailing: RawString::default(),
template,
strip: Strip::default(),
}
}
pub fn preamble(&self) -> &RawString {
&self.preamble
}
pub fn set_preamble(&mut self, preamble: impl Into<RawString>) {
self.preamble = preamble.into();
}
pub fn trailing(&self) -> &RawString {
&self.trailing
}
pub fn set_trailing(&mut self, trailing: impl Into<RawString>) {
self.trailing = trailing.into();
}
pub(crate) fn despan(&mut self, input: &str) {
self.preamble.despan(input);
self.template.despan(input);
self.trailing.despan(input);
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct EndifTemplateExpr {
pub strip: Strip,
preamble: RawString,
trailing: RawString,
}
impl EndifTemplateExpr {
pub fn new() -> EndifTemplateExpr {
EndifTemplateExpr::default()
}
pub fn preamble(&self) -> &RawString {
&self.preamble
}
pub fn set_preamble(&mut self, preamble: impl Into<RawString>) {
self.preamble = preamble.into();
}
pub fn trailing(&self) -> &RawString {
&self.trailing
}
pub fn set_trailing(&mut self, trailing: impl Into<RawString>) {
self.trailing = trailing.into();
}
pub(crate) fn despan(&mut self, input: &str) {
self.preamble.despan(input);
self.trailing.despan(input);
}
}
#[derive(Debug, Clone, Eq)]
pub struct ForDirective {
pub for_expr: ForTemplateExpr,
pub endfor_expr: EndforTemplateExpr,
span: Option<Range<usize>>,
}
impl ForDirective {
pub fn new(for_expr: ForTemplateExpr, endfor_expr: EndforTemplateExpr) -> ForDirective {
ForDirective {
for_expr,
endfor_expr,
span: None,
}
}
pub(crate) fn despan(&mut self, input: &str) {
self.for_expr.despan(input);
self.endfor_expr.despan(input);
}
}
impl PartialEq for ForDirective {
fn eq(&self, other: &Self) -> bool {
self.for_expr == other.for_expr && self.endfor_expr == other.endfor_expr
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ForTemplateExpr {
pub key_var: Option<Decorated<Ident>>,
pub value_var: Decorated<Ident>,
pub collection_expr: Expression,
pub template: Template,
pub strip: Strip,
preamble: RawString,
}
impl ForTemplateExpr {
pub fn new(
key_var: Option<impl Into<Decorated<Ident>>>,
value_var: impl Into<Decorated<Ident>>,
collection_expr: impl Into<Expression>,
template: Template,
) -> ForTemplateExpr {
ForTemplateExpr {
preamble: RawString::default(),
key_var: key_var.map(Into::into),
value_var: value_var.into(),
collection_expr: collection_expr.into(),
template,
strip: Strip::default(),
}
}
pub fn preamble(&self) -> &RawString {
&self.preamble
}
pub fn set_preamble(&mut self, preamble: impl Into<RawString>) {
self.preamble = preamble.into();
}
pub(crate) fn despan(&mut self, input: &str) {
self.preamble.despan(input);
if let Some(key_var) = &mut self.key_var {
key_var.decor_mut().despan(input);
}
self.value_var.decor_mut().despan(input);
self.collection_expr.despan(input);
self.template.despan(input);
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct EndforTemplateExpr {
pub strip: Strip,
preamble: RawString,
trailing: RawString,
}
impl EndforTemplateExpr {
pub fn new() -> EndforTemplateExpr {
EndforTemplateExpr::default()
}
pub fn preamble(&self) -> &RawString {
&self.preamble
}
pub fn set_preamble(&mut self, preamble: impl Into<RawString>) {
self.preamble = preamble.into();
}
pub fn trailing(&self) -> &RawString {
&self.trailing
}
pub fn set_trailing(&mut self, trailing: impl Into<RawString>) {
self.trailing = trailing.into();
}
pub(crate) fn despan(&mut self, input: &str) {
self.preamble.despan(input);
self.trailing.despan(input);
}
}
decorate_impl! { StringTemplate, HeredocTemplate }
span_impl! {
StringTemplate, HeredocTemplate, Template,
Interpolation, IfDirective, ForDirective
}
forward_span_impl! {
Element => { Literal, Interpolation, Directive },
Directive => { If, For }
}