Skip to content

Commit 2e524e6

Browse files
committed
Use typed-path in place of std Path/PathBuf
This allows handling path conversions in a more structured way, as well as avoiding needless UTF-8 checks. All argument inputs use `Utf8NativePathBuf`, while all config entries use `Utf8UnixPathBuf`, ensuring that we deserialize/serialize using forward slashes. We can omit `.display()` and lossy UTF-8 conversions since all paths are known valid UTF-8.
1 parent 64d0491 commit 2e524e6

35 files changed

+623
-599
lines changed

Cargo.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ publish = false
99
repository = "https://github.com/encounter/decomp-toolkit"
1010
readme = "README.md"
1111
categories = ["command-line-utilities"]
12-
rust-version = "1.80.0"
12+
rust-version = "1.81"
1313

1414
[[bin]]
1515
name = "dtk"
@@ -31,6 +31,7 @@ argp = "0.3"
3131
base16ct = "0.2"
3232
base64 = "0.22"
3333
byteorder = "1.5"
34+
typed-path = "0.9"
3435
crossterm = "0.28"
3536
cwdemangle = "1.0"
3637
cwextab = "1.0"
@@ -57,7 +58,6 @@ object = { version = "0.36", features = ["read_core", "std", "elf", "write_std"]
5758
once_cell = "1.20"
5859
orthrus-ncompress = "0.2"
5960
owo-colors = { version = "4.1", features = ["supports-colors"] }
60-
path-slash = "0.2"
6161
petgraph = { version = "0.6", default-features = false }
6262
ppc750cl = "0.3"
6363
rayon = "1.10"

src/cmd/alf.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
use std::{
2-
io::{stdout, Write},
3-
path::PathBuf,
4-
};
1+
use std::io::{stdout, Write};
52

63
use anyhow::Result;
74
use argp::FromArgs;
5+
use typed_path::Utf8NativePathBuf;
86

97
use crate::{
108
cmd,
119
util::{
1210
alf::AlfFile,
1311
file::buf_writer,
12+
path::native_path,
1413
reader::{Endian, FromReader},
1514
},
1615
vfs::open_file,
@@ -35,21 +34,21 @@ enum SubCommand {
3534
/// Prints information about an alf file. (Same as `dol info`)
3635
#[argp(subcommand, name = "info")]
3736
pub struct InfoArgs {
38-
#[argp(positional)]
37+
#[argp(positional, from_str_fn(native_path))]
3938
/// alf file
40-
file: PathBuf,
39+
file: Utf8NativePathBuf,
4140
}
4241

4342
#[derive(FromArgs, PartialEq, Debug)]
4443
/// Extracts symbol hashes from an alf file.
4544
#[argp(subcommand, name = "hashes")]
4645
pub struct HashesArgs {
47-
#[argp(positional)]
46+
#[argp(positional, from_str_fn(native_path))]
4847
/// alf file
49-
alf_file: PathBuf,
50-
#[argp(positional)]
48+
alf_file: Utf8NativePathBuf,
49+
#[argp(positional, from_str_fn(native_path))]
5150
/// output file
52-
output: Option<PathBuf>,
51+
output: Option<Utf8NativePathBuf>,
5352
}
5453

5554
pub fn run(args: Args) -> Result<()> {
@@ -64,7 +63,7 @@ fn hashes(args: HashesArgs) -> Result<()> {
6463
let mut file = open_file(&args.alf_file, true)?;
6564
AlfFile::from_reader(file.as_mut(), Endian::Little)?
6665
};
67-
let mut w: Box<dyn Write> = if let Some(output) = args.output {
66+
let mut w: Box<dyn Write> = if let Some(output) = &args.output {
6867
Box::new(buf_writer(output)?)
6968
} else {
7069
Box::new(stdout())

src/cmd/ar.rs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@ use std::{
22
collections::{btree_map::Entry, BTreeMap},
33
fs::File,
44
io::Write,
5-
path::PathBuf,
65
};
76

87
use anyhow::{anyhow, bail, Context, Result};
98
use argp::FromArgs;
109
use object::{Object, ObjectSymbol, SymbolScope};
10+
use typed_path::Utf8NativePathBuf;
1111

1212
use crate::{
13-
util::file::{buf_writer, process_rsp},
13+
util::{
14+
file::{buf_writer, process_rsp},
15+
path::native_path,
16+
},
1417
vfs::open_file,
1518
};
1619

@@ -33,24 +36,24 @@ enum SubCommand {
3336
/// Creates a static library.
3437
#[argp(subcommand, name = "create")]
3538
pub struct CreateArgs {
36-
#[argp(positional)]
39+
#[argp(positional, from_str_fn(native_path))]
3740
/// output file
38-
out: PathBuf,
39-
#[argp(positional)]
41+
out: Utf8NativePathBuf,
42+
#[argp(positional, from_str_fn(native_path))]
4043
/// input files
41-
files: Vec<PathBuf>,
44+
files: Vec<Utf8NativePathBuf>,
4245
}
4346

4447
#[derive(FromArgs, PartialEq, Eq, Debug)]
4548
/// Extracts a static library.
4649
#[argp(subcommand, name = "extract")]
4750
pub struct ExtractArgs {
48-
#[argp(positional)]
51+
#[argp(positional, from_str_fn(native_path))]
4952
/// input files
50-
files: Vec<PathBuf>,
51-
#[argp(option, short = 'o')]
53+
files: Vec<Utf8NativePathBuf>,
54+
#[argp(option, short = 'o', from_str_fn(native_path))]
5255
/// output directory
53-
out: Option<PathBuf>,
56+
out: Option<Utf8NativePathBuf>,
5457
#[argp(switch, short = 'q')]
5558
/// quiet output
5659
quiet: bool,
@@ -74,14 +77,13 @@ fn create(args: CreateArgs) -> Result<()> {
7477
let mut identifiers = Vec::with_capacity(files.len());
7578
let mut symbol_table = BTreeMap::new();
7679
for path in &files {
77-
let path_str =
78-
path.to_str().ok_or_else(|| anyhow!("'{}' is not valid UTF-8", path.display()))?;
79-
let identifier = path_str.as_bytes().to_vec();
80+
let unix_path = path.with_unix_encoding();
81+
let identifier = unix_path.as_str().as_bytes().to_vec();
8082
identifiers.push(identifier.clone());
8183

8284
let entries = match symbol_table.entry(identifier) {
8385
Entry::Vacant(e) => e.insert(Vec::new()),
84-
Entry::Occupied(_) => bail!("Duplicate file name '{path_str}'"),
86+
Entry::Occupied(_) => bail!("Duplicate file name '{unix_path}'"),
8587
};
8688
let mut file = open_file(path, false)?;
8789
let obj = object::File::parse(file.map()?)?;
@@ -102,10 +104,8 @@ fn create(args: CreateArgs) -> Result<()> {
102104
symbol_table,
103105
)?;
104106
for path in files {
105-
let path_str =
106-
path.to_str().ok_or_else(|| anyhow!("'{}' is not valid UTF-8", path.display()))?;
107107
let mut file = File::open(&path)?;
108-
builder.append_file(path_str.as_bytes(), &mut file)?;
108+
builder.append_file(path.as_str().as_bytes(), &mut file)?;
109109
}
110110
builder.into_inner()?.flush()?;
111111
Ok(())
@@ -118,22 +118,22 @@ fn extract(args: ExtractArgs) -> Result<()> {
118118
// Extract files
119119
let mut num_files = 0;
120120
for path in &files {
121-
let mut out_dir = if let Some(out) = &args.out { out.clone() } else { PathBuf::new() };
121+
let mut out_dir =
122+
if let Some(out) = &args.out { out.clone() } else { Utf8NativePathBuf::new() };
122123
// If there are multiple files, extract to separate directories
123124
if files.len() > 1 {
124125
out_dir
125126
.push(path.with_extension("").file_name().ok_or_else(|| anyhow!("No file name"))?);
126127
}
127128
std::fs::create_dir_all(&out_dir)?;
128129
if !args.quiet {
129-
println!("Extracting {} to {}", path.display(), out_dir.display());
130+
println!("Extracting {} to {}", path, out_dir);
130131
}
131132

132133
let mut file = open_file(path, false)?;
133134
let mut archive = ar::Archive::new(file.map()?);
134135
while let Some(entry) = archive.next_entry() {
135-
let mut entry =
136-
entry.with_context(|| format!("Processing entry in {}", path.display()))?;
136+
let mut entry = entry.with_context(|| format!("Processing entry in {}", path))?;
137137
let file_name = std::str::from_utf8(entry.header().identifier())?;
138138
if !args.quiet && args.verbose {
139139
println!("\t{}", file_name);
@@ -146,7 +146,7 @@ fn extract(args: ExtractArgs) -> Result<()> {
146146
std::fs::create_dir_all(parent)?;
147147
}
148148
let mut file = File::create(&file_path)
149-
.with_context(|| format!("Failed to create file {}", file_path.display()))?;
149+
.with_context(|| format!("Failed to create file {}", file_path))?;
150150
std::io::copy(&mut entry, &mut file)?;
151151
file.flush()?;
152152

0 commit comments

Comments
 (0)