Skip to content

Commit 33b00f9

Browse files
committed
feat(cli): allow applying one mask to multiple targets
1 parent 30a5b52 commit 33b00f9

6 files changed

Lines changed: 70 additions & 57 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ Use a mask to assign an icon to a folder:
2929
folderify mask.png /path/to/folder
3030
```
3131

32+
Use the same mask for multiple folders in one run:
33+
34+
```shell
35+
folderify mask.png /path/to/folder-1 /path/to/folder-2 /path/to/folder-3
36+
```
37+
3238
Generate `mask.icns` and `mask.iconset` files:
3339

3440
```shell

src/args.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ struct FolderifyArgs {
2323
#[clap(verbatim_doc_comment)]
2424
mask: Option<PathBuf>,
2525

26-
/// Target file or folder. If a target is specified, the resulting icon will
27-
/// be applied to the target file/folder. Else (unless --output-icns or
26+
/// Target files or folders. If any targets are specified, the resulting icon
27+
/// will be applied to each target. Else (unless --output-icns or
2828
/// --output-iconset is specified), a .iconset folder and .icns file will be
2929
/// created in the same folder as the mask (you can use "Get Info" in Finder
3030
/// to copy the icon from the .icns file).
3131
#[clap(verbatim_doc_comment)]
32-
target: Option<PathBuf>,
32+
targets: Vec<PathBuf>,
3333

3434
/// Write the `.icns` file to the given path.
3535
/// (Will be written even if a target is also specified.)
@@ -142,7 +142,7 @@ pub struct Options {
142142
pub mask_path: PathBuf,
143143
pub color_scheme: ColorScheme,
144144
pub no_trim: bool,
145-
pub target: Option<PathBuf>,
145+
pub targets: Vec<PathBuf>,
146146
pub folder_style: FolderStyle,
147147
pub empty_folder: bool,
148148
pub output_icns: Option<PathBuf>,
@@ -263,7 +263,7 @@ pub fn get_options() -> Options {
263263
mask_path: mask,
264264
color_scheme: map_color_scheme_auto(args.color_scheme, folder_style),
265265
no_trim: args.no_trim,
266-
target: args.target,
266+
targets: args.targets,
267267
folder_style,
268268
empty_folder: args.empty_folder,
269269
output_icns: args.output_icns,

src/icon_conversion.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ impl ProgressBarType {
2323
ProgressBarType::Input => 1,
2424
ProgressBarType::Conversion => 13 + if options.badge.is_some() { 1 } else { 0 },
2525
ProgressBarType::OutputWithAssignment => {
26-
2 + if matches!(options.set_icon_using, SetIconUsing::Rez) {
27-
7
28-
} else {
29-
0
30-
}
26+
let assignment_steps = match options.set_icon_using {
27+
SetIconUsing::Rez => 7,
28+
_ => 1,
29+
};
30+
1 + assignment_steps * options.targets.len() as u64
3131
}
3232
ProgressBarType::OutputIcns => 1,
3333
}
@@ -651,7 +651,7 @@ impl IconConversion {
651651
eprintln!("Icon was not successfully assigned to the target folder.");
652652
exit(1);
653653
}
654-
} else if options.target.is_some() {
654+
} else if !options.targets.is_empty() {
655655
// TODO: this is usually overwritten by the progress bars.
656656
eprintln!(
657657
"Target is not a folder. Please check manually if the icon was assigned correctly."

src/main.rs

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,12 @@ fn main() {
9292
handles.push(handle);
9393
}
9494

95-
let output_iconset_only = match (
96-
&options.target,
97-
&options.output_icns,
98-
&options.output_iconset,
99-
) {
100-
(None, None, Some(output_iconset)) => Some(output_iconset),
101-
_ => None,
95+
let output_iconset_only = if options.targets.is_empty()
96+
&& options.output_icns.is_none()
97+
{
98+
options.output_iconset.as_ref()
99+
} else {
100+
None
102101
};
103102

104103
// Deduplicate this `match` with the one that happens after handle joining.
@@ -133,20 +132,16 @@ fn main() {
133132
)
134133
.unwrap();
135134

136-
let icns_assignment_path = options
137-
.target
138-
.as_ref()
139-
.unwrap_or(&final_output_paths.icns_path);
135+
for target in &options.targets {
136+
output_icon_conversion
137+
.assign_icns(&options, &final_output_paths.icns_path, target)
138+
.unwrap();
139+
}
140140

141-
output_icon_conversion
142-
.assign_icns(
143-
&options,
144-
&final_output_paths.icns_path,
145-
icns_assignment_path,
146-
)
147-
.unwrap();
148-
149-
icns_assignment_path
141+
options
142+
.targets
143+
.first()
144+
.unwrap_or(&final_output_paths.icns_path)
150145
}
151146
};
152147

src/output_paths.rs

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,38 +20,42 @@ impl PotentialOutputPaths {
2020
iconset_dir: None,
2121
icns_path: None,
2222
};
23-
match (
24-
&options.target,
25-
&options.output_iconset,
26-
&options.output_icns,
27-
) {
28-
(Some(target), output_iconset, output_icns) => {
23+
if !options.targets.is_empty() {
24+
for target in &options.targets {
2925
println!(
3026
"[{}] => assign to [{}]",
3127
options.mask_path.display(),
3228
target.display()
3329
);
34-
Self::alt_outputs(options, &mut output_paths, output_iconset, output_icns);
35-
}
36-
(None, None, None) => {
37-
let iconset_dir_value = options.mask_path.with_extension("iconset");
38-
let icns_path_value = options.mask_path.with_extension("icns");
39-
println!(
40-
"[{}] => [{}]",
41-
options.mask_path.display(),
42-
iconset_dir_value.display()
43-
);
44-
println!(
45-
"[{}] => [{}]",
46-
options.mask_path.display(),
47-
icns_path_value.display()
48-
);
49-
output_paths.iconset_dir = Some(iconset_dir_value);
50-
output_paths.icns_path = Some(icns_path_value);
51-
}
52-
(None, output_iconset, output_icns) => {
53-
Self::alt_outputs(options, &mut output_paths, output_iconset, output_icns);
5430
}
31+
Self::alt_outputs(
32+
options,
33+
&mut output_paths,
34+
&options.output_iconset,
35+
&options.output_icns,
36+
);
37+
} else if options.output_iconset.is_none() && options.output_icns.is_none() {
38+
let iconset_dir_value = options.mask_path.with_extension("iconset");
39+
let icns_path_value = options.mask_path.with_extension("icns");
40+
println!(
41+
"[{}] => [{}]",
42+
options.mask_path.display(),
43+
iconset_dir_value.display()
44+
);
45+
println!(
46+
"[{}] => [{}]",
47+
options.mask_path.display(),
48+
icns_path_value.display()
49+
);
50+
output_paths.iconset_dir = Some(iconset_dir_value);
51+
output_paths.icns_path = Some(icns_path_value);
52+
} else {
53+
Self::alt_outputs(
54+
options,
55+
&mut output_paths,
56+
&options.output_iconset,
57+
&options.output_icns,
58+
);
5559
}
5660
output_paths
5761
}

test/test-behaviour.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ test("Assign folder icon", async () => {
5959
expect(await tempDir.join("Icon\r").exists()).toBe(true);
6060
});
6161

62+
test("Assign the same folder icon to multiple targets", async () => {
63+
const tempDir1 = await Path.makeTempDir();
64+
const tempDir2 = await Path.makeTempDir();
65+
await shellOut([EXAMPLES.join("src/apple.png"), tempDir1, tempDir2]);
66+
expect(await tempDir1.join("Icon\r").exists()).toBe(true);
67+
expect(await tempDir2.join("Icon\r").exists()).toBe(true);
68+
});
69+
6270
test("Assign folder icon using Rez", async () => {
6371
const tempDir = await Path.makeTempDir();
6472
await shellOut([

0 commit comments

Comments
 (0)