#![allow(missing_docs)]
use crate::{
BoxSource, CachedSource, ConcatSource, OriginalSource, RawBufferSource,
RawStringSource, ReplaceSource, ReplacementEnforce, Source, SourceExt,
SourceMap, SourceMapSource, SourceMapSourceOptions,
};
#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub struct CacheableReplacement {
pub start: u32,
pub end: u32,
pub content: String,
pub name: Option<String>,
pub enforce: u8,
}
#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
#[rkyv(serialize_bounds(
__S: rkyv::ser::Writer + rkyv::ser::Allocator + rkyv::rancor::Fallible
))]
#[rkyv(deserialize_bounds(
__D: rkyv::rancor::Fallible<Error: rkyv::rancor::Source>
))]
#[rkyv(bytecheck(bounds(
__C: rkyv::validation::ArchiveContext + rkyv::rancor::Fallible
)))]
pub enum CacheableSource {
RawBuffer {
buffer: Vec<u8>,
},
RawString {
value: String,
},
Original {
value: String,
name: String,
},
SourceMap {
value: String,
name: String,
source_map: String,
original_source: Option<String>,
inner_source_map: Option<String>,
remove_original_source: bool,
},
Concat {
#[rkyv(omit_bounds)]
children: Vec<CacheableSource>,
},
Replace {
#[rkyv(omit_bounds)]
inner: Box<CacheableSource>,
replacements: Vec<CacheableReplacement>,
},
Cached {
#[rkyv(omit_bounds)]
inner: Box<CacheableSource>,
},
}
pub fn to_cacheable(source: &dyn Source) -> CacheableSource {
if let Some(s) = source.as_any().downcast_ref::<CachedSource>() {
return CacheableSource::Cached {
inner: Box::new(to_cacheable(s.inner().as_ref())),
};
}
if let Some(s) = source.as_any().downcast_ref::<OriginalSource>() {
return CacheableSource::Original {
value: s.value().to_string(),
name: s.name().to_string(),
};
}
if let Some(s) = source.as_any().downcast_ref::<RawStringSource>() {
return CacheableSource::RawString {
value: s.source().into_string_lossy().into_owned(),
};
}
if let Some(s) = source.as_any().downcast_ref::<RawBufferSource>() {
return CacheableSource::RawBuffer {
buffer: s.buffer().into_owned(),
};
}
if let Some(s) = source.as_any().downcast_ref::<SourceMapSource>() {
return CacheableSource::SourceMap {
value: s.value().to_string(),
name: s.name().to_string(),
source_map: s.source_map().to_json(),
original_source: s.original_source().map(|v| v.to_string()),
inner_source_map: s.inner_source_map().map(|m| m.to_json()),
remove_original_source: s.remove_original_source(),
};
}
if let Some(s) = source.as_any().downcast_ref::<ConcatSource>() {
return CacheableSource::Concat {
children: s
.children()
.iter()
.map(|c| to_cacheable(c.as_ref()))
.collect(),
};
}
if let Some(s) = source.as_any().downcast_ref::<ReplaceSource>() {
let replacements = s
.replacements()
.iter()
.map(|r| CacheableReplacement {
start: r.start(),
end: r.end(),
content: r.content().to_string(),
name: r.name().map(|n| n.to_string()),
enforce: match r.enforce() {
ReplacementEnforce::Pre => 0,
ReplacementEnforce::Normal => 1,
ReplacementEnforce::Post => 2,
},
})
.collect();
return CacheableSource::Replace {
inner: Box::new(to_cacheable(s.inner().as_ref())),
replacements,
};
}
panic!(
"Unexpected source type in persistent cache serialization. All BoxSource instances should be one of the known rspack_sources types."
)
}
pub fn from_cacheable(cacheable: CacheableSource) -> BoxSource {
match cacheable {
CacheableSource::RawBuffer { buffer } => {
RawBufferSource::from(buffer).boxed()
}
CacheableSource::RawString { value } => {
RawStringSource::from(value).boxed()
}
CacheableSource::Original { value, name } => {
OriginalSource::new(value, name).boxed()
}
CacheableSource::SourceMap {
value,
name,
source_map,
original_source,
inner_source_map,
remove_original_source,
} => {
let source_map =
SourceMap::from_json(&source_map).expect("invalid source map JSON");
let inner_source_map =
inner_source_map.and_then(|json| SourceMap::from_json(&json).ok());
SourceMapSource::new(SourceMapSourceOptions {
value,
name,
source_map,
original_source: original_source.map(|s| s.into()),
inner_source_map,
remove_original_source,
})
.boxed()
}
CacheableSource::Concat { children } => {
let children: Vec<BoxSource> =
children.into_iter().map(from_cacheable).collect();
ConcatSource::new(children).boxed()
}
CacheableSource::Replace {
inner,
replacements,
} => {
let inner = from_cacheable(*inner);
let mut source = ReplaceSource::new(inner);
for r in replacements {
let enforce = match r.enforce {
0 => ReplacementEnforce::Pre,
1 => ReplacementEnforce::Normal,
2 => ReplacementEnforce::Post,
_ => {
panic!("Invalid enforce value in cached replacement: {}", r.enforce)
}
};
source.replace_with_enforce(r.start, r.end, r.content, r.name, enforce);
}
source.boxed()
}
CacheableSource::Cached { inner } => {
CachedSource::new(from_cacheable(*inner)).boxed()
}
}
}