Skip to main content

rustpython_ruff_python_ast/
str_prefix.rs

1use ruff_text_size::TextSize;
2
3use std::fmt;
4
5/// Enumerations of the valid prefixes a string literal can have.
6///
7/// Bytestrings and f-strings are excluded from this enumeration,
8/// as they are represented by different AST nodes.
9#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, is_macro::Is)]
10pub enum StringLiteralPrefix {
11    /// Just a regular string with no prefixes
12    Empty,
13
14    /// A string with a `u` or `U` prefix, e.g. `u"foo"`.
15    /// Note that, despite this variant's name,
16    /// it is in fact a no-op at runtime to use the `u` or `U` prefix
17    /// in Python. All Python-3 strings are unicode strings;
18    /// this prefix is only allowed in Python 3 for backwards compatibility
19    /// with Python 2. However, using this prefix in a Python string
20    /// is mutually exclusive with an `r` or `R` prefix.
21    Unicode,
22
23    /// A "raw" string, that has an `r` or `R` prefix,
24    /// e.g. `r"foo\."` or `R'bar\d'`.
25    Raw { uppercase: bool },
26}
27
28impl StringLiteralPrefix {
29    /// Return a `str` representation of the prefix
30    pub const fn as_str(self) -> &'static str {
31        match self {
32            Self::Empty => "",
33            Self::Unicode => "u",
34            Self::Raw { uppercase: true } => "R",
35            Self::Raw { uppercase: false } => "r",
36        }
37    }
38
39    pub const fn text_len(self) -> TextSize {
40        match self {
41            Self::Empty => TextSize::new(0),
42            Self::Unicode | Self::Raw { .. } => TextSize::new(1),
43        }
44    }
45}
46
47impl fmt::Display for StringLiteralPrefix {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        f.write_str(self.as_str())
50    }
51}
52
53/// Enumeration of the valid prefixes an f-string literal can have.
54#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
55pub enum FStringPrefix {
56    /// Just a regular f-string with no other prefixes, e.g. f"{bar}"
57    Regular,
58
59    /// A "raw" format-string, that has an `r` or `R` prefix,
60    /// e.g. `rf"{bar}"` or `Rf"{bar}"`
61    Raw { uppercase_r: bool },
62}
63
64impl FStringPrefix {
65    /// Return a `str` representation of the prefix
66    pub const fn as_str(self) -> &'static str {
67        match self {
68            Self::Regular => "f",
69            Self::Raw { uppercase_r: true } => "Rf",
70            Self::Raw { uppercase_r: false } => "rf",
71        }
72    }
73
74    pub const fn text_len(self) -> TextSize {
75        match self {
76            Self::Regular => TextSize::new(1),
77            Self::Raw { .. } => TextSize::new(2),
78        }
79    }
80
81    /// Return true if this prefix indicates a "raw f-string",
82    /// e.g. `rf"{bar}"` or `Rf"{bar}"`
83    pub const fn is_raw(self) -> bool {
84        matches!(self, Self::Raw { .. })
85    }
86}
87
88impl fmt::Display for FStringPrefix {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        f.write_str(self.as_str())
91    }
92}
93
94/// Enumeration of the valid prefixes a t-string literal can have.
95#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
96pub enum TStringPrefix {
97    /// Just a regular t-string with no other prefixes, e.g. t"{bar}"
98    Regular,
99
100    /// A "raw" template string, that has an `r` or `R` prefix,
101    /// e.g. `rt"{bar}"` or `Rt"{bar}"`
102    Raw { uppercase_r: bool },
103}
104
105impl TStringPrefix {
106    /// Return a `str` representation of the prefix
107    pub const fn as_str(self) -> &'static str {
108        match self {
109            Self::Regular => "t",
110            Self::Raw { uppercase_r: true } => "Rt",
111            Self::Raw { uppercase_r: false } => "rt",
112        }
113    }
114
115    pub const fn text_len(self) -> TextSize {
116        match self {
117            Self::Regular => TextSize::new(1),
118            Self::Raw { .. } => TextSize::new(2),
119        }
120    }
121
122    /// Return true if this prefix indicates a "raw t-string",
123    /// e.g. `rt"{bar}"` or `Rt"{bar}"`
124    pub const fn is_raw(self) -> bool {
125        matches!(self, Self::Raw { .. })
126    }
127}
128
129impl fmt::Display for TStringPrefix {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        f.write_str(self.as_str())
132    }
133}
134
135/// Enumeration of the valid prefixes a bytestring literal can have.
136#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
137pub enum ByteStringPrefix {
138    /// Just a regular bytestring with no other prefixes, e.g. `b"foo"`
139    Regular,
140
141    /// A "raw" bytestring, that has an `r` or `R` prefix,
142    /// e.g. `Rb"foo"` or `rb"foo"`
143    Raw { uppercase_r: bool },
144}
145
146impl ByteStringPrefix {
147    /// Return a `str` representation of the prefix
148    pub const fn as_str(self) -> &'static str {
149        match self {
150            Self::Regular => "b",
151            Self::Raw { uppercase_r: true } => "Rb",
152            Self::Raw { uppercase_r: false } => "rb",
153        }
154    }
155
156    pub const fn text_len(self) -> TextSize {
157        match self {
158            Self::Regular => TextSize::new(1),
159            Self::Raw { .. } => TextSize::new(2),
160        }
161    }
162
163    /// Return true if this prefix indicates a "raw bytestring",
164    /// e.g. `rb"foo"` or `Rb"foo"`
165    pub const fn is_raw(self) -> bool {
166        matches!(self, Self::Raw { .. })
167    }
168}
169
170impl fmt::Display for ByteStringPrefix {
171    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172        f.write_str(self.as_str())
173    }
174}
175
176/// Enumeration of all the possible valid prefixes
177/// prior to a Python string literal.
178///
179/// Using the `as_flags()` method on variants of this enum
180/// is the recommended way to set `*_PREFIX` flags from the
181/// `StringFlags` bitflag, as it means that you cannot accidentally
182/// set a combination of `*_PREFIX` flags that would be invalid
183/// at runtime in Python.
184///
185/// [String and Bytes literals]: https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals
186/// [PEP 701]: https://peps.python.org/pep-0701/
187#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, is_macro::Is)]
188pub enum AnyStringPrefix {
189    /// Prefixes that indicate the string is a bytestring
190    Bytes(ByteStringPrefix),
191
192    /// Prefixes that indicate the string is an f-string
193    Format(FStringPrefix),
194
195    /// Prefixes that indicate the string is a t-string
196    Template(TStringPrefix),
197
198    /// All other prefixes
199    Regular(StringLiteralPrefix),
200}
201
202impl AnyStringPrefix {
203    pub const fn as_str(self) -> &'static str {
204        match self {
205            Self::Regular(regular_prefix) => regular_prefix.as_str(),
206            Self::Bytes(bytestring_prefix) => bytestring_prefix.as_str(),
207            Self::Format(fstring_prefix) => fstring_prefix.as_str(),
208            Self::Template(tstring_prefix) => tstring_prefix.as_str(),
209        }
210    }
211
212    pub const fn text_len(self) -> TextSize {
213        match self {
214            Self::Regular(regular_prefix) => regular_prefix.text_len(),
215            Self::Bytes(bytestring_prefix) => bytestring_prefix.text_len(),
216            Self::Format(fstring_prefix) => fstring_prefix.text_len(),
217            Self::Template(tstring_prefix) => tstring_prefix.text_len(),
218        }
219    }
220
221    pub const fn is_raw(self) -> bool {
222        match self {
223            Self::Regular(regular_prefix) => regular_prefix.is_raw(),
224            Self::Bytes(bytestring_prefix) => bytestring_prefix.is_raw(),
225            Self::Format(fstring_prefix) => fstring_prefix.is_raw(),
226            Self::Template(tstring_prefix) => tstring_prefix.is_raw(),
227        }
228    }
229}
230
231impl fmt::Display for AnyStringPrefix {
232    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233        f.write_str(self.as_str())
234    }
235}
236
237impl Default for AnyStringPrefix {
238    fn default() -> Self {
239        Self::Regular(StringLiteralPrefix::Empty)
240    }
241}