use super::{LoadError, SourceName};
use crate::parser::{css, sassfile, Span};
use crate::{Error, ParseError};
use std::io::Read;
use std::sync::Arc;
#[derive(Clone)]
pub struct SourceFile {
data: Arc<Impl>,
}
struct Impl {
data: Vec<u8>,
source: SourceName,
format: SourceFormat,
}
impl SourceFile {
pub fn read<T: Read>(
file: &mut T,
source: SourceName,
) -> Result<Self, LoadError> {
let format = SourceFormat::try_from(source.name())?;
let mut data = vec![];
file.read_to_end(&mut data)
.map_err(|e| LoadError::Input(source.name().to_string(), e))?;
Ok(Self::new(data, source, format))
}
pub fn scss_bytes(data: impl Into<Vec<u8>>, source: SourceName) -> Self {
Self::new(data.into(), source, SourceFormat::Scss)
}
pub fn css_bytes(data: impl Into<Vec<u8>>, source: SourceName) -> Self {
Self::new(data.into(), source, SourceFormat::Css)
}
fn new(data: Vec<u8>, source: SourceName, format: SourceFormat) -> Self {
Self {
data: Arc::new(Impl {
data,
source,
format,
}),
}
}
pub fn parse(&self) -> Result<Parsed, Error> {
let data = Span::new(self);
match self.data.format {
SourceFormat::Scss => {
Ok(Parsed::Scss(ParseError::check(sassfile(data))?))
}
SourceFormat::Css => {
Ok(Parsed::Css(ParseError::check(css::file(data))?))
}
}
}
pub(crate) fn data(&self) -> &[u8] {
&self.data.data
}
pub(crate) fn source(&self) -> &SourceName {
&self.data.source
}
pub(crate) fn path(&self) -> &str {
self.data.source.name()
}
}
impl Ord for SourceFile {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.data.source.cmp(&other.data.source)
}
}
impl PartialOrd for SourceFile {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for SourceFile {
fn eq(&self, other: &Self) -> bool {
self.data.source == other.data.source
}
}
impl Eq for SourceFile {}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum SourceFormat {
Scss,
Css,
}
impl TryFrom<&str> for SourceFormat {
type Error = LoadError;
fn try_from(name: &str) -> Result<Self, LoadError> {
if name.ends_with(".scss") {
Ok(Self::Scss)
} else if name.ends_with(".css") {
Ok(Self::Css)
} else {
Err(LoadError::UnknownFormat(name.into()))
}
}
}
#[derive(Clone, Debug)]
pub enum Parsed {
Css(Vec<crate::css::Item>),
Scss(Vec<crate::sass::Item>),
}