use crate::error::{IoError, IoErrorKind, KrikError, KrikResult};
use crate::theme::Theme;
use std::fs;
use std::path::Path;
use walkdir::WalkDir;
fn is_ignored_asset(path: &Path) -> bool {
if let Some(file_name_os) = path.file_name() {
if let Some(file_name) = file_name_os.to_str() {
if file_name.starts_with('.') {
return true;
}
let lower = file_name.to_lowercase();
if lower == "thumbs.db" {
return true;
}
if lower.ends_with('~') {
return true;
}
if lower.starts_with('#') && lower.ends_with('#') {
return true;
}
if let Some(ext) = path
.extension()
.and_then(|s| s.to_str())
.map(|s| s.to_lowercase())
{
match ext.as_str() {
"swp" | "swo" | "swx" | "tmp" | "bak" | "orig" | "part" | "crdownload" => {
return true
}
_ => {}
}
}
}
}
false
}
pub fn copy_non_markdown_files(source_dir: &Path, output_dir: &Path) -> KrikResult<()> {
for entry in WalkDir::new(source_dir)
.follow_links(true)
.into_iter()
.filter_map(|e| e.ok())
{
let path = entry.path();
if entry.file_type().is_dir() || path.extension().is_some_and(|ext| ext == "md") {
continue;
}
if path.file_name() == Some(std::ffi::OsStr::new("site.toml")) {
continue;
}
if is_ignored_asset(path) {
continue;
}
let relative_path = path.strip_prefix(source_dir).map_err(|_| {
KrikError::Io(Box::new(IoError {
kind: IoErrorKind::InvalidPath,
path: path.to_path_buf(),
context: format!(
"Computing relative path from {} to {}",
source_dir.display(),
path.display()
),
}))
})?;
let dest_path = output_dir.join(relative_path);
if let Some(parent) = dest_path.parent() {
fs::create_dir_all(parent).map_err(|e| {
KrikError::Io(Box::new(IoError {
kind: IoErrorKind::WriteFailed(e),
path: parent.to_path_buf(),
context: "Creating parent directories for asset copy".to_string(),
}))
})?;
}
fs::copy(path, &dest_path).map_err(|e| {
KrikError::Io(Box::new(IoError {
kind: IoErrorKind::WriteFailed(e),
path: dest_path.clone(),
context: format!("Copying asset from {}", path.display()),
}))
})?;
}
Ok(())
}
pub fn copy_theme_assets(theme: &Theme, output_dir: &Path) -> KrikResult<()> {
let asset_dir = theme.theme_path.join("assets");
if asset_dir.exists() {
let dest_assets_dir = output_dir.join("assets");
fs::create_dir_all(&dest_assets_dir).map_err(|e| {
KrikError::Io(Box::new(IoError {
kind: IoErrorKind::WriteFailed(e),
path: dest_assets_dir.clone(),
context: "Creating destination assets directory".to_string(),
}))
})?;
copy_directory_contents(&asset_dir, &dest_assets_dir)?;
}
Ok(())
}
fn copy_directory_contents(src: &Path, dest: &Path) -> KrikResult<()> {
for entry in WalkDir::new(src)
.follow_links(true)
.into_iter()
.filter_map(|e| e.ok())
{
if !entry.file_type().is_file() {
continue;
}
let path = entry.path();
if is_ignored_asset(path) {
continue;
}
let relative_path = path.strip_prefix(src).map_err(|_| {
KrikError::Io(Box::new(IoError {
kind: IoErrorKind::InvalidPath,
path: path.to_path_buf(),
context: format!(
"Computing relative path from {} to {}",
src.display(),
path.display()
),
}))
})?;
let dest_path = dest.join(relative_path);
if let Some(parent) = dest_path.parent() {
fs::create_dir_all(parent).map_err(|e| {
KrikError::Io(Box::new(IoError {
kind: IoErrorKind::WriteFailed(e),
path: parent.to_path_buf(),
context: "Creating parent directories for theme asset copy".to_string(),
}))
})?;
}
fs::copy(path, &dest_path).map_err(|e| {
KrikError::Io(Box::new(IoError {
kind: IoErrorKind::WriteFailed(e),
path: dest_path.clone(),
context: format!("Copying theme asset from {}", path.display()),
}))
})?;
}
Ok(())
}
pub fn copy_single_asset(source_dir: &Path, output_dir: &Path, file_path: &Path) -> KrikResult<()> {
if !file_path.exists() || file_path.is_dir() {
return Ok(());
}
if file_path.extension().is_some_and(|ext| ext == "md") {
return Ok(());
}
if file_path.file_name() == Some(std::ffi::OsStr::new("site.toml")) {
return Ok(());
}
if is_ignored_asset(file_path) {
return Ok(());
}
let relative_path = file_path.strip_prefix(source_dir).map_err(|_| {
KrikError::Io(Box::new(IoError {
kind: IoErrorKind::InvalidPath,
path: file_path.to_path_buf(),
context: format!(
"Computing relative path from {} to {}",
source_dir.display(),
file_path.display()
),
}))
})?;
let dest_path = output_dir.join(relative_path);
if let Some(parent) = dest_path.parent() {
fs::create_dir_all(parent).map_err(|e| {
KrikError::Io(Box::new(IoError {
kind: IoErrorKind::WriteFailed(e),
path: parent.to_path_buf(),
context: "Creating parent directories for single asset copy".to_string(),
}))
})?;
}
fs::copy(file_path, &dest_path).map_err(|e| {
KrikError::Io(Box::new(IoError {
kind: IoErrorKind::WriteFailed(e),
path: dest_path.clone(),
context: format!("Copying single asset from {}", file_path.display()),
}))
})?;
Ok(())
}
pub fn remove_single_asset(
source_dir: &Path,
output_dir: &Path,
file_path: &Path,
) -> KrikResult<()> {
let relative_path = match file_path.strip_prefix(source_dir) {
Ok(rel) => rel,
Err(_) => return Ok(()),
};
let dest_path = output_dir.join(relative_path);
if dest_path.exists() && dest_path.is_file() {
let _ = fs::remove_file(dest_path);
}
Ok(())
}