darklua_core/nodes/expressions/
interpolated_string.rsuse std::iter::FromIterator;
use crate::nodes::{StringError, Token, Trivia};
use super::{string_utils, Expression};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StringSegment {
value: String,
token: Option<Token>,
}
impl StringSegment {
pub fn new(value: impl AsRef<str>) -> Result<Self, StringError> {
string_utils::read_escaped_string(value.as_ref().char_indices(), None).map(Self::from_value)
}
pub fn from_value(value: impl Into<String>) -> Self {
Self {
value: value.into(),
token: None,
}
}
pub fn with_token(mut self, token: Token) -> Self {
self.token = Some(token);
self
}
pub fn set_token(&mut self, token: Token) {
self.token = Some(token);
}
pub fn get_token(&self) -> Option<&Token> {
self.token.as_ref()
}
pub fn get_value(&self) -> &str {
self.value.as_str()
}
fn append(&mut self, mut other: Self) {
self.value.extend(other.value.drain(..));
self.token = None;
}
#[inline]
pub fn len(&self) -> usize {
self.value.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.value.is_empty()
}
super::impl_token_fns!(iter = [token]);
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ValueSegment {
value: Expression,
tokens: Option<ValueSegmentTokens>,
}
impl ValueSegment {
pub fn new(value: impl Into<Expression>) -> Self {
Self {
value: value.into(),
tokens: None,
}
}
pub fn with_tokens(mut self, tokens: ValueSegmentTokens) -> Self {
self.tokens = Some(tokens);
self
}
pub fn set_tokens(&mut self, tokens: ValueSegmentTokens) {
self.tokens = Some(tokens);
}
pub fn get_tokens(&self) -> Option<&ValueSegmentTokens> {
self.tokens.as_ref()
}
pub fn get_expression(&self) -> &Expression {
&self.value
}
pub fn mutate_expression(&mut self) -> &mut Expression {
&mut self.value
}
super::impl_token_fns!(iter = [tokens]);
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ValueSegmentTokens {
pub opening_brace: Token,
pub closing_brace: Token,
}
impl ValueSegmentTokens {
super::impl_token_fns!(target = [opening_brace, closing_brace]);
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InterpolationSegment {
String(StringSegment),
Value(ValueSegment),
}
impl InterpolationSegment {
pub fn clear_comments(&mut self) {
match self {
InterpolationSegment::String(segment) => segment.clear_comments(),
InterpolationSegment::Value(segment) => segment.clear_comments(),
}
}
pub fn clear_whitespaces(&mut self) {
match self {
InterpolationSegment::String(segment) => segment.clear_whitespaces(),
InterpolationSegment::Value(segment) => segment.clear_whitespaces(),
}
}
pub(crate) fn replace_referenced_tokens(&mut self, code: &str) {
match self {
InterpolationSegment::String(segment) => segment.replace_referenced_tokens(code),
InterpolationSegment::Value(segment) => segment.replace_referenced_tokens(code),
}
}
pub(crate) fn shift_token_line(&mut self, amount: usize) {
match self {
InterpolationSegment::String(segment) => segment.shift_token_line(amount),
InterpolationSegment::Value(segment) => segment.shift_token_line(amount),
}
}
pub(crate) fn filter_comments(&mut self, filter: impl Fn(&Trivia) -> bool) {
match self {
InterpolationSegment::String(segment) => segment.filter_comments(filter),
InterpolationSegment::Value(segment) => segment.filter_comments(filter),
}
}
}
impl From<StringSegment> for InterpolationSegment {
fn from(segment: StringSegment) -> Self {
Self::String(segment)
}
}
impl From<ValueSegment> for InterpolationSegment {
fn from(segment: ValueSegment) -> Self {
Self::Value(segment)
}
}
impl<T: Into<Expression>> From<T> for InterpolationSegment {
fn from(value: T) -> Self {
Self::Value(ValueSegment::new(value.into()))
}
}
impl From<&str> for InterpolationSegment {
fn from(string: &str) -> Self {
Self::String(StringSegment::from_value(string))
}
}
impl From<&String> for InterpolationSegment {
fn from(string: &String) -> Self {
Self::String(StringSegment::from_value(string))
}
}
impl From<String> for InterpolationSegment {
fn from(string: String) -> Self {
Self::String(StringSegment::from_value(string))
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InterpolatedStringExpression {
segments: Vec<InterpolationSegment>,
tokens: Option<InterpolatedStringTokens>,
}
impl InterpolatedStringExpression {
pub fn new(segments: Vec<InterpolationSegment>) -> Self {
Self {
segments,
tokens: None,
}
}
pub fn empty() -> Self {
Self::new(Vec::default())
}
pub fn with_segment(mut self, segment: impl Into<InterpolationSegment>) -> Self {
self.push_segment(segment);
self
}
pub fn with_tokens(mut self, tokens: InterpolatedStringTokens) -> Self {
self.tokens = Some(tokens);
self
}
pub fn get_tokens(&self) -> Option<&InterpolatedStringTokens> {
self.tokens.as_ref()
}
pub fn set_tokens(&mut self, tokens: InterpolatedStringTokens) {
self.tokens = Some(tokens);
}
super::impl_token_fns!(iter = [tokens, segments]);
pub fn iter_segments(&self) -> impl Iterator<Item = &InterpolationSegment> {
self.segments.iter()
}
pub fn iter_mut_segments(&mut self) -> impl Iterator<Item = &mut InterpolationSegment> {
self.segments.iter_mut()
}
#[inline]
pub fn len(&self) -> usize {
self.segments.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.segments.iter().all(|segment| match segment {
InterpolationSegment::String(string_segment) => string_segment.is_empty(),
InterpolationSegment::Value(_) => false,
})
}
pub fn push_segment(&mut self, segment: impl Into<InterpolationSegment>) {
let new_segment = segment.into();
match new_segment {
InterpolationSegment::String(string_segment) => {
if string_segment.get_value().is_empty() {
return;
}
if let Some(InterpolationSegment::String(last)) = self.segments.last_mut() {
last.append(string_segment);
} else {
self.segments.push(string_segment.into());
}
}
InterpolationSegment::Value(_) => {
self.segments.push(new_segment);
}
}
}
}
impl FromIterator<InterpolationSegment> for InterpolatedStringExpression {
fn from_iter<T: IntoIterator<Item = InterpolationSegment>>(iter: T) -> Self {
Self {
segments: iter.into_iter().collect(),
tokens: None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InterpolatedStringTokens {
pub opening_tick: Token,
pub closing_tick: Token,
}
impl InterpolatedStringTokens {
super::impl_token_fns!(target = [opening_tick, closing_tick]);
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn push_segment_with_empty_string_does_not_mutate() {
let mut string = InterpolatedStringExpression::empty();
string.push_segment("");
pretty_assertions::assert_eq!(string, InterpolatedStringExpression::empty());
}
}