1use std::{
2 borrow::Cow,
3 hash::{Hash, Hasher},
4 sync::OnceLock,
5};
6
7use crate::{
8 helpers::{
9 get_generated_source_info, stream_chunks_of_raw_source, OnChunk, OnName,
10 OnSource, StreamChunks,
11 },
12 MapOptions, Rope, Source, SourceMap,
13};
14
15#[derive(Clone, PartialEq, Eq)]
16enum RawValue {
17 Buffer(Vec<u8>),
18 String(Cow<'static, str>),
19}
20
21#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
22static_assertions::assert_eq_size!(RawValue, [u8; 32]);
23
24pub struct RawSource {
38 value: RawValue,
39 value_as_string: OnceLock<String>,
40}
41
42impl RawSource {
43 pub fn from_static(s: &'static str) -> Self {
53 Self {
54 value: RawValue::String(Cow::Borrowed(s)),
55 value_as_string: Default::default(),
56 }
57 }
58}
59
60impl Clone for RawSource {
61 fn clone(&self) -> Self {
62 Self {
63 value: self.value.clone(),
64 value_as_string: Default::default(),
65 }
66 }
67}
68
69impl Eq for RawSource {}
70
71impl RawSource {
72 pub fn is_buffer(&self) -> bool {
74 matches!(self.value, RawValue::Buffer(_))
75 }
76}
77
78impl From<String> for RawSource {
79 fn from(value: String) -> Self {
80 Self {
81 value: RawValue::String(value.into()),
82 value_as_string: Default::default(),
83 }
84 }
85}
86
87impl From<Vec<u8>> for RawSource {
88 fn from(value: Vec<u8>) -> Self {
89 Self {
90 value: RawValue::Buffer(value),
91 value_as_string: Default::default(),
92 }
93 }
94}
95
96impl From<&str> for RawSource {
97 fn from(value: &str) -> Self {
98 Self {
99 value: RawValue::String(value.to_string().into()),
100 value_as_string: Default::default(),
101 }
102 }
103}
104
105impl From<&[u8]> for RawSource {
106 fn from(value: &[u8]) -> Self {
107 Self {
108 value: RawValue::Buffer(value.to_owned()),
109 value_as_string: Default::default(),
110 }
111 }
112}
113
114impl Source for RawSource {
115 fn source(&self) -> Cow<str> {
116 match &self.value {
117 RawValue::String(v) => Cow::Borrowed(v),
118 RawValue::Buffer(v) => Cow::Borrowed(
119 self
120 .value_as_string
121 .get_or_init(|| String::from_utf8_lossy(v).to_string()),
122 ),
123 }
124 }
125
126 fn rope(&self) -> Rope<'_> {
127 match &self.value {
128 RawValue::Buffer(v) => Rope::from(
129 self
130 .value_as_string
131 .get_or_init(|| String::from_utf8_lossy(v).to_string()),
132 ),
133 RawValue::String(s) => Rope::from(s),
134 }
135 }
136
137 fn buffer(&self) -> Cow<[u8]> {
138 match &self.value {
139 RawValue::String(v) => Cow::Borrowed(v.as_bytes()),
140 RawValue::Buffer(v) => Cow::Borrowed(v),
141 }
142 }
143
144 fn size(&self) -> usize {
145 match &self.value {
146 RawValue::String(v) => v.len(),
147 RawValue::Buffer(v) => v.len(),
148 }
149 }
150
151 fn map(&self, _: &MapOptions) -> Option<SourceMap> {
152 None
153 }
154
155 fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
156 writer.write_all(match &self.value {
157 RawValue::String(v) => v.as_bytes(),
158 RawValue::Buffer(v) => v,
159 })
160 }
161}
162
163impl Hash for RawSource {
164 fn hash<H: Hasher>(&self, state: &mut H) {
165 "RawSource".hash(state);
166 self.buffer().hash(state);
167 }
168}
169
170impl PartialEq for RawSource {
171 fn eq(&self, other: &Self) -> bool {
172 match (&self.value, &other.value) {
173 (RawValue::Buffer(l0), RawValue::Buffer(r0)) => l0 == r0,
174 (RawValue::String(l0), RawValue::String(r0)) => l0 == r0,
175 _ => false,
176 }
177 }
178}
179
180impl std::fmt::Debug for RawSource {
181 fn fmt(
182 &self,
183 f: &mut std::fmt::Formatter<'_>,
184 ) -> Result<(), std::fmt::Error> {
185 let mut d = f.debug_struct("RawSource");
186 match &self.value {
187 RawValue::Buffer(buffer) => {
188 d.field(
189 "buffer",
190 &buffer.iter().take(50).copied().collect::<Vec<u8>>(),
191 );
192 }
193 RawValue::String(string) => {
194 d.field("source", &string.chars().take(50).collect::<String>());
195 }
196 }
197 d.finish()
198 }
199}
200
201impl StreamChunks for RawSource {
202 fn stream_chunks<'a>(
203 &'a self,
204 options: &MapOptions,
205 on_chunk: OnChunk<'_, 'a>,
206 on_source: OnSource<'_, 'a>,
207 on_name: OnName<'_, 'a>,
208 ) -> crate::helpers::GeneratedInfo {
209 if options.final_source {
210 match &self.value {
211 RawValue::Buffer(buffer) => {
212 let source = self
213 .value_as_string
214 .get_or_init(|| String::from_utf8_lossy(buffer).to_string());
215 get_generated_source_info(&**source)
216 }
217 RawValue::String(source) => get_generated_source_info(&**source),
218 }
219 } else {
220 match &self.value {
221 RawValue::Buffer(buffer) => {
222 let source = self
223 .value_as_string
224 .get_or_init(|| String::from_utf8_lossy(buffer).to_string());
225 stream_chunks_of_raw_source(
226 &**source, options, on_chunk, on_source, on_name,
227 )
228 }
229 RawValue::String(source) => stream_chunks_of_raw_source(
230 &**source, options, on_chunk, on_source, on_name,
231 ),
232 }
233 }
234 }
235}
236
237#[derive(Clone, PartialEq, Eq)]
251pub struct RawStringSource(Cow<'static, str>);
252
253#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
254static_assertions::assert_eq_size!(RawStringSource, [u8; 24]);
255
256impl RawStringSource {
257 pub fn from_static(s: &'static str) -> Self {
267 Self(Cow::Borrowed(s))
268 }
269}
270
271impl From<String> for RawStringSource {
272 fn from(value: String) -> Self {
273 Self(Cow::Owned(value))
274 }
275}
276
277impl From<&str> for RawStringSource {
278 fn from(value: &str) -> Self {
279 Self(Cow::Owned(value.to_owned()))
280 }
281}
282
283impl Source for RawStringSource {
284 fn source(&self) -> Cow<str> {
285 Cow::Borrowed(&self.0)
286 }
287
288 fn rope(&self) -> Rope<'_> {
289 Rope::from(&self.0)
290 }
291
292 fn buffer(&self) -> Cow<[u8]> {
293 Cow::Borrowed(self.0.as_bytes())
294 }
295
296 fn size(&self) -> usize {
297 self.0.len()
298 }
299
300 fn map(&self, _: &MapOptions) -> Option<SourceMap> {
301 None
302 }
303
304 fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
305 writer.write_all(self.0.as_bytes())
306 }
307}
308
309impl std::fmt::Debug for RawStringSource {
310 fn fmt(
311 &self,
312 f: &mut std::fmt::Formatter<'_>,
313 ) -> Result<(), std::fmt::Error> {
314 let mut d = f.debug_tuple("RawStringSource");
315 d.field(&self.0.chars().take(50).collect::<String>());
316 d.finish()
317 }
318}
319
320impl Hash for RawStringSource {
321 fn hash<H: Hasher>(&self, state: &mut H) {
322 "RawStringSource".hash(state);
323 self.buffer().hash(state);
324 }
325}
326
327impl StreamChunks for RawStringSource {
328 fn stream_chunks<'a>(
329 &'a self,
330 options: &MapOptions,
331 on_chunk: OnChunk<'_, 'a>,
332 on_source: OnSource<'_, 'a>,
333 on_name: OnName<'_, 'a>,
334 ) -> crate::helpers::GeneratedInfo {
335 if options.final_source {
336 get_generated_source_info(&*self.0)
337 } else {
338 stream_chunks_of_raw_source(
339 &*self.0, options, on_chunk, on_source, on_name,
340 )
341 }
342 }
343}
344
345#[derive(Clone, PartialEq, Eq)]
359pub struct RawBufferSource {
360 value: Vec<u8>,
361 value_as_string: OnceLock<String>,
362}
363
364impl From<Vec<u8>> for RawBufferSource {
365 fn from(value: Vec<u8>) -> Self {
366 Self {
367 value,
368 value_as_string: Default::default(),
369 }
370 }
371}
372
373impl From<&[u8]> for RawBufferSource {
374 fn from(value: &[u8]) -> Self {
375 Self {
376 value: value.to_vec(),
377 value_as_string: Default::default(),
378 }
379 }
380}
381
382impl Source for RawBufferSource {
383 fn source(&self) -> Cow<str> {
384 Cow::Borrowed(
385 self
386 .value_as_string
387 .get_or_init(|| String::from_utf8_lossy(&self.value).to_string()),
388 )
389 }
390
391 fn rope(&self) -> Rope<'_> {
392 Rope::from(
393 self
394 .value_as_string
395 .get_or_init(|| String::from_utf8_lossy(&self.value).to_string()),
396 )
397 }
398
399 fn buffer(&self) -> Cow<[u8]> {
400 Cow::Borrowed(&self.value)
401 }
402
403 fn size(&self) -> usize {
404 self.value.len()
405 }
406
407 fn map(&self, _: &MapOptions) -> Option<SourceMap> {
408 None
409 }
410
411 fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
412 writer.write_all(&self.value)
413 }
414}
415
416impl std::fmt::Debug for RawBufferSource {
417 fn fmt(
418 &self,
419 f: &mut std::fmt::Formatter<'_>,
420 ) -> Result<(), std::fmt::Error> {
421 let mut d = f.debug_tuple("RawBufferSource");
422 d.field(&self.value.iter().take(50).copied().collect::<Vec<u8>>());
423 d.finish()
424 }
425}
426
427impl Hash for RawBufferSource {
428 fn hash<H: Hasher>(&self, state: &mut H) {
429 "RawBufferSource".hash(state);
430 self.buffer().hash(state);
431 }
432}
433
434impl StreamChunks for RawBufferSource {
435 fn stream_chunks<'a>(
436 &'a self,
437 options: &MapOptions,
438 on_chunk: OnChunk<'_, 'a>,
439 on_source: OnSource<'_, 'a>,
440 on_name: OnName<'_, 'a>,
441 ) -> crate::helpers::GeneratedInfo {
442 if options.final_source {
443 get_generated_source_info(&*self.source())
444 } else {
445 stream_chunks_of_raw_source(
446 &**self
447 .value_as_string
448 .get_or_init(|| String::from_utf8_lossy(&self.value).to_string()),
449 options,
450 on_chunk,
451 on_source,
452 on_name,
453 )
454 }
455 }
456}
457
458#[cfg(test)]
459mod tests {
460 use crate::{ConcatSource, OriginalSource, ReplaceSource, SourceExt};
461
462 use super::*;
463
464 #[test]
466 fn fix_rustbolt_issue_6793() {
467 let source1 = RawSource::from("hello\n\n".to_string());
468 let source1 = ReplaceSource::new(source1);
469 let source2 = OriginalSource::new("world".to_string(), "world.txt");
470 let concat = ConcatSource::new([source1.boxed(), source2.boxed()]);
471 let map = concat.map(&MapOptions::new(false)).unwrap();
472 assert_eq!(map.mappings(), ";;AAAA",);
473 }
474}