use crate::Result;
use anyhow::anyhow;
use deno_ast::MediaType;
use deno_core::ModuleSpecifier;
use encoding_rs::Encoding;
pub use http_cache::HttpCache;
pub use mashin_core::{
colors,
sdk::{ResourceAction, Urn},
ExecutedResource,
};
use std::{
borrow::Cow,
collections::HashMap,
fs::OpenOptions,
io::{Error, ErrorKind, Write},
path::{Path, PathBuf},
sync::Arc,
};
mod http_cache;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct SourceFile {
pub local: PathBuf,
pub maybe_types: Option<String>,
pub media_type: MediaType,
pub source: Arc<str>,
pub specifier: ModuleSpecifier,
pub maybe_headers: Option<HashMap<String, String>>,
}
pub const CACHE_PERM: u32 = 0o644;
pub fn atomic_write_file<T: AsRef<[u8]>>(
filename: &Path,
data: T,
mode: u32,
) -> std::io::Result<()> {
let rand: String = (0..4).map(|_| format!("{:02x}", rand::random::<u8>())).collect();
let extension = format!("{rand}.tmp");
let tmp_file = filename.with_extension(extension);
write_file(&tmp_file, data, mode)?;
std::fs::rename(tmp_file, filename)?;
Ok(())
}
pub fn write_file<T: AsRef<[u8]>>(filename: &Path, data: T, mode: u32) -> std::io::Result<()> {
write_file_2(filename, data, true, mode, true, false)
}
pub fn write_file_2<T: AsRef<[u8]>>(
filename: &Path,
data: T,
update_mode: bool,
mode: u32,
is_create: bool,
is_append: bool,
) -> std::io::Result<()> {
let mut file = OpenOptions::new()
.read(false)
.write(true)
.append(is_append)
.truncate(!is_append)
.create(is_create)
.open(filename)?;
if update_mode {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mode = mode & 0o777;
let permissions = PermissionsExt::from_mode(mode);
file.set_permissions(permissions)?;
}
#[cfg(not(unix))]
let _ = mode;
}
file.write_all(data.as_ref())
}
pub fn get_source_from_bytes(bytes: Vec<u8>, maybe_charset: Option<String>) -> Result<String> {
let source = if let Some(charset) = maybe_charset {
convert_to_utf8(&bytes, &charset)?.to_string()
} else {
String::from_utf8(bytes)?
};
Ok(source)
}
fn convert_to_utf8<'a>(bytes: &'a [u8], charset: &'_ str) -> Result<Cow<'a, str>> {
match Encoding::for_label(charset.as_bytes()) {
Some(encoding) => encoding
.decode_without_bom_handling_and_without_replacement(bytes)
.ok_or_else(|| anyhow!("invalid data")),
None =>
Err(Error::new(ErrorKind::InvalidInput, format!("Unsupported charset: {charset}"))
.into()),
}
}