Files
tour/src/utils.rs
2026-03-07 14:50:09 +00:00

110 lines
3.1 KiB
Rust

use std::fs;
use std::io;
use std::path::Path;
use crate::error::TourError;
use crate::SESSION_PATH;
use crate::TOUR_DIR;
pub fn require_tour() -> Result<(), TourError> {
if !Path::new(TOUR_DIR).exists() {
return Err(TourError::NoTour);
}
Ok(())
}
pub fn get_current_step() -> Option<u32> {
fs::read_to_string(SESSION_PATH)
.ok()
.and_then(|s| {
s.lines()
.find_map(|l| l.strip_prefix("STEP="))
.and_then(|v| v.trim().parse::<u32>().ok())
})
}
pub fn get_tour_step() -> Result<u32, TourError> {
let steps_dir = Path::new(TOUR_DIR).join("steps");
let mut indices: Vec<u32> = fs::read_dir(&steps_dir)?
.filter_map(|e| e.ok())
.filter(|e| e.path().is_dir())
.filter_map(|e| e.file_name().to_str()?.parse::<u32>().ok())
.collect();
if indices.is_empty() {
return Ok(0);
}
indices.sort();
let count = indices.len() as u32;
for (i, &idx) in indices.iter().enumerate() {
if idx != i as u32 {
return Err(TourError::CorruptedTour(
format!("step directories are not sequential (expected {}, found {})", i, idx),
));
}
}
Ok(count)
}
/// Copies a file or directory into dest_dir, preserving relative path structure.
pub fn copy_path(src: &Path, dest_dir: &Path) -> Result<(), io::Error> {
let relative_src = if src.is_absolute() {
let cwd = std::env::current_dir()?;
src.strip_prefix(&cwd)
.map_err(|_| io::Error::new(
io::ErrorKind::InvalidInput,
format!("path '{}' is not under the current directory", src.display()),
))?
.to_path_buf()
} else {
src.to_path_buf()
};
if src.is_dir() {
let dest = dest_dir.join(&relative_src);
fs::create_dir_all(&dest)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
copy_path(&entry.path(), dest_dir)?;
}
} else {
let dest = dest_dir.join(&relative_src);
if let Some(parent) = dest.parent() {
fs::create_dir_all(parent)?;
}
fs::copy(src, &dest)?;
}
Ok(())
}
/// Recursively copies src to dest. If src is a directory, copies its contents
/// into dest. If src is a file, copies it to dest.
pub fn copy_tree(src: &Path, dest: &Path) -> Result<(), io::Error> {
if src.is_dir() {
fs::create_dir_all(dest)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
copy_tree(&entry.path(), &dest.join(entry.file_name()))?;
}
} else {
if let Some(parent) = dest.parent() {
fs::create_dir_all(parent)?;
}
fs::copy(src, dest)?;
}
Ok(())
}
pub fn is_descendant_of_current_dir(file: &Path) -> Result<bool, io::Error> {
is_file_in_dir(file, &std::env::current_dir()?)
}
pub fn is_file_in_dir(file: &Path, dir: &Path) -> Result<bool, io::Error> {
let file_canon = file.canonicalize()?;
let dir_canon = dir.canonicalize()?;
Ok(file_canon.starts_with(&dir_canon))
}