1use serde::Deserialize;
2use serde::Serialize;
3use strum::Display;
4
5use mago_interner::StringIdentifier;
6use mago_span::HasSpan;
7use mago_span::Span;
8
9use crate::ast::ast::expression::Expression;
10use crate::ast::sequence::Sequence;
11
12#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord, Display)]
13#[serde(tag = "type", content = "value")]
14#[repr(C, u8)]
15pub enum CompositeString {
16 ShellExecute(ShellExecuteString),
17 Interpolated(InterpolatedString),
18 Document(DocumentString),
19}
20
21#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
22#[repr(C)]
23pub struct ShellExecuteString {
24 pub left_backtick: Span,
25 pub parts: Sequence<StringPart>,
26 pub right_backtick: Span,
27}
28
29#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
30#[repr(C)]
31pub struct InterpolatedString {
32 pub left_double_quote: Span,
33 pub parts: Sequence<StringPart>,
34 pub right_double_quote: Span,
35}
36
37#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord, Display)]
38#[serde(tag = "type", content = "value")]
39#[repr(C)]
40pub enum DocumentKind {
41 Heredoc,
42 Nowdoc,
43}
44
45#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord, Display)]
46#[serde(tag = "type", content = "value")]
47#[repr(C, u8)]
48pub enum DocumentIndentation {
49 None,
50 Whitespace(usize),
51 Tab(usize),
52 Mixed(usize, usize),
53}
54
55#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
56#[repr(C)]
57pub struct DocumentString {
58 pub open: Span,
59 pub kind: DocumentKind,
60 pub indentation: DocumentIndentation,
61 pub label: StringIdentifier,
62 pub parts: Sequence<StringPart>,
63 pub close: Span,
64}
65
66#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord, Display)]
67#[serde(tag = "type", content = "value")]
68#[repr(C, u8)]
69pub enum StringPart {
70 Literal(LiteralStringPart),
71 Expression(Box<Expression>),
72 BracedExpression(BracedExpressionStringPart),
73}
74
75#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
76#[repr(C)]
77pub struct LiteralStringPart {
78 pub span: Span,
79 pub value: StringIdentifier,
80}
81
82#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
83#[repr(C)]
84pub struct BracedExpressionStringPart {
85 pub left_brace: Span,
86 pub expression: Box<Expression>,
87 pub right_brace: Span,
88}
89
90impl CompositeString {
91 pub fn parts(&self) -> &Sequence<StringPart> {
92 match self {
93 CompositeString::ShellExecute(s) => &s.parts,
94 CompositeString::Interpolated(i) => &i.parts,
95 CompositeString::Document(d) => &d.parts,
96 }
97 }
98}
99
100impl HasSpan for CompositeString {
101 fn span(&self) -> Span {
102 match self {
103 CompositeString::ShellExecute(s) => s.span(),
104 CompositeString::Interpolated(i) => i.span(),
105 CompositeString::Document(d) => d.span(),
106 }
107 }
108}
109
110impl HasSpan for ShellExecuteString {
111 fn span(&self) -> Span {
112 self.left_backtick.join(self.right_backtick)
113 }
114}
115
116impl HasSpan for InterpolatedString {
117 fn span(&self) -> Span {
118 self.left_double_quote.join(self.right_double_quote)
119 }
120}
121
122impl HasSpan for DocumentString {
123 fn span(&self) -> Span {
124 self.open
125 }
126}
127
128impl HasSpan for StringPart {
129 fn span(&self) -> Span {
130 match self {
131 StringPart::Literal(l) => l.span(),
132 StringPart::Expression(e) => e.span(),
133 StringPart::BracedExpression(b) => b.span(),
134 }
135 }
136}
137
138impl HasSpan for LiteralStringPart {
139 fn span(&self) -> Span {
140 self.span
141 }
142}
143
144impl HasSpan for BracedExpressionStringPart {
145 fn span(&self) -> Span {
146 self.left_brace.join(self.right_brace)
147 }
148}