Skip to content

Commit

Permalink
cli/web: Fix output extension for directory inputs with dots in the n…
Browse files Browse the repository at this point in the history
…ame (#89)
  • Loading branch information
antangelo authored Sep 7, 2024
1 parent 60479f1 commit b073e8c
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 22 deletions.
4 changes: 3 additions & 1 deletion xdvdfs-cli/src/cmd_build_image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use xdvdfs::write::{
img::ProgressInfo,
};

use crate::img::with_extension;

#[derive(Args)]
#[command(
about = "Pack an image from a given specification",
Expand Down Expand Up @@ -201,7 +203,7 @@ pub async fn cmd_build_image(args: &BuildImageArgs) -> Result<(), anyhow::Error>
if let Some(output) = output_path {
source_path.join(output)
} else {
source_path.with_extension("xiso.iso")
with_extension(&source_path, "xiso.iso", true)
}
};

Expand Down
19 changes: 13 additions & 6 deletions xdvdfs-cli/src/cmd_compress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use xdvdfs::{
write::{self, img::ProgressInfo},
};

use crate::img::open_image_raw;
use crate::img::{open_image_raw, with_extension};

#[derive(Args)]
#[command(about = "Pack and compress an image from a given directory or source ISO image")]
Expand Down Expand Up @@ -44,29 +44,37 @@ impl ciso::split::SplitFilesystem<std::io::Error, BufFile> for SplitStdFs {
async fn close(&mut self, _: BufFile) {}
}

fn get_default_image_path(source_path: &Path) -> Option<PathBuf> {
fn get_default_image_path(source_path: &Path, is_dir: bool) -> Option<PathBuf> {
let source_file_name = source_path.file_name()?;
let output = PathBuf::from(source_file_name).with_extension("cso");
let output = with_extension(Path::new(source_file_name), "cso", is_dir);

Some(output)
}

#[maybe_async]
pub async fn cmd_compress(args: &CompressArgs) -> Result<(), anyhow::Error> {
let source_path = PathBuf::from(&args.source_path);
let meta = std::fs::metadata(&source_path)?;
let is_dir = meta.is_dir();

let image_path = args
.image_path
.as_ref()
.map(PathBuf::from)
.unwrap_or_else(|| get_default_image_path(&source_path).unwrap());
.unwrap_or_else(|| get_default_image_path(&source_path, is_dir).unwrap());

// This is unlikely to happen, since compressed input is unsupported
// and this will fail anyway, but we check to avoid truncating the input accidentally
if image_path.exists() && image_path.canonicalize()? == source_path {
return Err(anyhow::anyhow!("Source and destination paths are the same"));
}

if image_path.starts_with(&source_path) {
return Err(anyhow::anyhow!(
"Destination path is contained by source path"
));
}

let mut output = ciso::split::SplitOutput::new(SplitStdFs, image_path);

let progress_callback = |pi| match pi {
Expand Down Expand Up @@ -94,8 +102,7 @@ pub async fn cmd_compress(args: &CompressArgs) -> Result<(), anyhow::Error> {
_ => {}
};

let meta = std::fs::metadata(&source_path)?;
if meta.is_dir() {
if is_dir {
let mut fs = write::fs::StdFilesystem::create(&source_path);
let mut slbd = write::fs::SectorLinearBlockDevice::default();
let mut slbfs: write::fs::SectorLinearBlockFilesystem<
Expand Down
27 changes: 20 additions & 7 deletions xdvdfs-cli/src/cmd_pack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use clap::Args;
use maybe_async::maybe_async;
use xdvdfs::write::{self, img::ProgressInfo};

use crate::img::with_extension;

#[derive(Args)]
#[command(about = "Pack an image from a given directory or source ISO image")]
pub struct PackArgs {
Expand All @@ -14,14 +16,18 @@ pub struct PackArgs {
image_path: Option<String>,
}

fn get_default_image_path(source_path: &Path) -> Result<PathBuf, anyhow::Error> {
fn get_default_image_path(source_path: &Path, is_dir: bool) -> Result<PathBuf, anyhow::Error> {
let source_file_name = source_path
.file_name()
.ok_or(anyhow::anyhow!("Failed to get file name from source path"))?;
let output = PathBuf::from(source_file_name).with_extension("iso");
let output = with_extension(Path::new(source_file_name), "iso", is_dir);

if output.exists() && output.canonicalize()? == source_path {
return Ok(PathBuf::from(source_file_name).with_extension("xiso.iso"));
return Ok(with_extension(
Path::new(source_file_name),
"xiso.iso",
is_dir,
));
}

Ok(output)
Expand All @@ -30,13 +36,15 @@ fn get_default_image_path(source_path: &Path) -> Result<PathBuf, anyhow::Error>
#[maybe_async]
pub async fn cmd_pack(args: &PackArgs) -> Result<(), anyhow::Error> {
let source_path = PathBuf::from(&args.source_path).canonicalize()?;
let meta = std::fs::metadata(&source_path)?;
let is_dir = meta.is_dir();

let image_path = args
.image_path
.as_ref()
.map(PathBuf::from)
.map(Ok)
.unwrap_or_else(|| get_default_image_path(&source_path))?;
.unwrap_or_else(|| get_default_image_path(&source_path, is_dir))?;

if image_path.exists() && image_path.canonicalize()? == source_path {
return Err(anyhow::anyhow!("Source and destination paths are the same"));
Expand All @@ -46,9 +54,15 @@ pub async fn cmd_pack(args: &PackArgs) -> Result<(), anyhow::Error> {
.write(true)
.truncate(true)
.create(true)
.open(image_path)?;
.open(&image_path)?;
let mut image = std::io::BufWriter::with_capacity(1024 * 1024, image);

if image_path.canonicalize()?.starts_with(&source_path) {
return Err(anyhow::anyhow!(
"Destination path is contained by source path"
));
}

let mut file_count: usize = 0;
let mut progress_count: usize = 0;
let progress_callback = |pi| match pi {
Expand All @@ -65,8 +79,7 @@ pub async fn cmd_pack(args: &PackArgs) -> Result<(), anyhow::Error> {
_ => {}
};

let meta = std::fs::metadata(&source_path)?;
if meta.is_dir() {
if is_dir {
let mut fs = write::fs::StdFilesystem::create(&source_path);
write::img::create_xdvdfs_image(&mut fs, &mut image, progress_callback).await?;
} else if meta.is_file() {
Expand Down
55 changes: 54 additions & 1 deletion xdvdfs-cli/src/img.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use ciso::read::CSOReader;
use maybe_async::maybe_async;
use std::{fs::File, io::BufReader, path::Path};
use std::{
fs::File,
io::BufReader,
path::{Path, PathBuf},
};
use xdvdfs::blockdev::{BlockDeviceRead, OffsetWrapper};

pub struct CSOBlockDevice<R: ciso::read::Read<std::io::Error>> {
Expand Down Expand Up @@ -71,3 +75,52 @@ pub async fn open_image(
Ok(image)
}
}

/// Similar to Path::with_extension, but will not overwrite the extension for
/// directories
// TODO: Replace with `Path::with_added_extension` after it stabilizes
pub fn with_extension(path: &Path, ext: &str, is_dir: bool) -> PathBuf {
if !is_dir {
return path.with_extension(ext);
}

let original_ext = path.extension();
let Some(original_ext) = original_ext else {
return path.with_extension(ext);
};

let mut new_ext = original_ext.to_owned();
new_ext.push(".");
new_ext.push(ext);
path.with_extension(new_ext)
}

#[cfg(test)]
mod test {
use super::with_extension;
use std::path::Path;

#[test]
fn with_extension_not_dir() {
assert_eq!(
with_extension(Path::new("file.abc"), "xyz", false),
Path::new("file.xyz")
);
}

#[test]
fn with_extension_dir_no_extension() {
assert_eq!(
with_extension(Path::new("dir"), "xyz", true),
Path::new("dir.xyz")
);
}

#[test]
fn with_extension_dir_with_extension() {
assert_eq!(
with_extension(Path::new("dir.abc"), "xyz", true),
Path::new("dir.abc.xyz")
);
}
}
40 changes: 33 additions & 7 deletions xdvdfs-web/src/packing.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::PathBuf;
use std::path::{Path, PathBuf};

use crate::ops::XDVDFSOperations;
use crate::picker::FilePickerBackend;
Expand All @@ -9,6 +9,25 @@ use xdvdfs::write::img::ProgressInfo;
use yew::prelude::*;
use yewprint::{Button, ButtonGroup, Callout, Icon, Intent, ProgressBar, H5};

/// Similar to Path::with_extension, but will not overwrite the extension for
/// directories
// TODO: Replace with `Path::with_added_extension` after it stabilizes
pub fn with_extension(path: &Path, ext: &str, is_dir: bool) -> PathBuf {
if !is_dir {
return path.with_extension(ext);
}

let original_ext = path.extension();
let Some(original_ext) = original_ext else {
return path.with_extension(ext);
};

let mut new_ext = original_ext.to_owned();
new_ext.push(".");
new_ext.push(ext);
path.with_extension(new_ext)
}

#[derive(PartialEq, PartialOrd, Copy, Clone)]
pub enum ImageCreationState {
CreatingFilesystem,
Expand Down Expand Up @@ -173,6 +192,10 @@ where
PickerResult::FileHandle(fh) => FPB::file_name(fh),
})
}

fn is_input_directory(&self) -> bool {
matches!(self.input_handle_type, Some(InputHandleType::Directory))
}
}

impl<FPB, XO> Component for ImageBuilderWorkflow<FPB, XO>
Expand Down Expand Up @@ -231,12 +254,15 @@ where
<FilePickerButton<FPB>
kind={PickerKind::SaveFile(
self.input_name().map(|name|
PathBuf::from(name)
.with_extension("xiso.iso")
.file_name()
.and_then(|name| name.to_str())
.map(|name| name.to_owned())
.expect("file name should be defined")
with_extension(
Path::new(&name),
"xiso.iso",
self.is_input_directory(),
)
.file_name()
.and_then(|name| name.to_str())
.map(|name| name.to_owned())
.expect("file name should be defined")
))}
button_text={"Save image"}
disabled={is_packing}
Expand Down

0 comments on commit b073e8c

Please sign in to comment.