Skip to content

Commit ac5b8ad

Browse files
committed
Add save subcommand
1 parent f2502d9 commit ac5b8ad

6 files changed

Lines changed: 145 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## Unreleased
22

3+
### Added
4+
- New subcommand: `save`, which saves blueprints as a .json file, or as a directory of json files for blueprint books
5+
36
### Changed
47
- `upgrade-quality` works on combinators
58
- `upgrade-quality` now errors when encountering something that looks like a quality that it doesn't know if it should upgrade.

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ base64 = "0.22.1"
1010
clap = { version = "4.5.42", features = ["derive"] }
1111
crossterm = { version = "0.29.0", features = ["osc52"] }
1212
flate2 = { version = "1.1.2", default-features = false, features = ["zlib-rs"] }
13+
sanitize-filename = "0.6.0"
1314
serde = { version = "1.0.219", features = ["derive"] }
1415
serde_json = "1.0.142"
1516
zlib-rs = "0.5.1"

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Commands:
1010
unwrap Unwraps a blueprint string to reveal the json representation
1111
wrap Wraps json from stdin into a blueprint string
1212
upgrade-quality Upgrades the quality of recipies/filters/conditions without upgrading entities/modules
13+
save Saves blueprint as a .json file, or as a directory of json files if it's a blueprint book
1314
help Print this message or the help of the given subcommand(s)
1415
1516
Options:

src/main.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
mod blueprint;
22
mod json_walk;
3+
mod save;
34

4-
use std::{
5-
fmt::Write as _,
6-
io::{Read, stderr, stdin},
7-
};
5+
use std::fmt::Write;
6+
use std::io::{Read, stderr, stdin};
7+
use std::str::FromStr;
88

99
use blueprint::{blueprint_to_json, json_to_blueprint};
1010
use clap::{Parser, Subcommand};
@@ -43,6 +43,8 @@ enum Commands {
4343
to_clipboard: bool,
4444
blueprint_string: Option<String>,
4545
},
46+
/// Saves blueprint as a .json file, or as a directory of json files if it's a blueprint book.
47+
Save { blueprint_string: Option<String> },
4648
}
4749

4850
mod terminal;
@@ -131,6 +133,13 @@ impl Commands {
131133
println!("{bp}");
132134
}
133135
}
136+
Commands::Save { blueprint_string } => {
137+
let blueprint_string = blueprint_string.unwrap_or_else(terminal::prompt_blueprint);
138+
let json = blueprint_to_json(&blueprint_string);
139+
let json = serde_json::Value::from_str(&json).expect("should contain valid json");
140+
141+
save::save(json, None);
142+
}
134143
}
135144
}
136145
}

src/save.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
use std::{
2+
fs::File,
3+
io::{BufWriter, Write},
4+
mem,
5+
path::{Path, PathBuf},
6+
};
7+
8+
pub fn save(mut json: serde_json::Value, dir: Option<&Path>) {
9+
let mut name = if let Some(thing) = json.get("blueprint").or(json.get("blueprint_book"))
10+
&& let Some(name) = thing.get("label")
11+
&& let Some(name) = name.as_str()
12+
{
13+
name.to_owned()
14+
} else if let Some(blueprint) = json.get("blueprint")
15+
&& let Some(icons) = blueprint.get("icons")
16+
&& let Some(icons) = icons.as_array()
17+
{
18+
let mut name = String::new();
19+
for icon in icons {
20+
if let Some(signal) = icon.get("signal")
21+
&& let Some(signal_name) = signal.get("name")
22+
&& let Some(signal_name) = signal_name.as_str()
23+
{
24+
if !name.is_empty() {
25+
name.push(' ');
26+
}
27+
name.push_str(signal_name);
28+
}
29+
}
30+
if !name.is_empty() {
31+
name
32+
} else {
33+
"Untitled".to_owned()
34+
}
35+
} else {
36+
"Untitled".to_owned()
37+
};
38+
39+
if let Some(index) = json.get_mut("index")
40+
&& let Some(index) = index.as_number()
41+
{
42+
name = format!("{index} {name}");
43+
}
44+
45+
// replace problematic characters like "/"
46+
name = sanitize_filename::sanitize_with_options(
47+
name,
48+
sanitize_filename::Options {
49+
windows: true,
50+
truncate: true,
51+
replacement: "_",
52+
},
53+
);
54+
55+
let file_path: PathBuf = if let Some(blueprint_book) = json.get_mut("blueprint_book")
56+
&& let Some(blueprints) = blueprint_book.get_mut("blueprints")
57+
&& let Some(blueprints) = blueprints.as_array_mut()
58+
{
59+
let blueprints = mem::replace(blueprints, vec![]);
60+
let path = if let Some(dir) = dir {
61+
dir.join(&name)
62+
} else {
63+
name.clone().into()
64+
};
65+
std::fs::create_dir(&path)
66+
.unwrap_or_else(|e| panic!("error creating book directory {path:?}: {e}"));
67+
for blueprint in blueprints {
68+
save(blueprint, Some(&path));
69+
}
70+
path.join("book.json")
71+
} else {
72+
let file = format!("{name}.json");
73+
if let Some(dir) = dir {
74+
dir.join(&file)
75+
} else {
76+
file.clone().into()
77+
}
78+
};
79+
80+
{
81+
let out_file = File::create_new(&file_path)
82+
.unwrap_or_else(|e| panic!("error creating file {file_path:?}: {e:?}"));
83+
let mut writer = BufWriter::new(out_file);
84+
serde_json::to_writer_pretty(&mut writer, &json).expect("error writing {file_path}");
85+
writer.flush().expect("error writing {file_path}");
86+
}
87+
println!("{file_path:?} saved.");
88+
}

0 commit comments

Comments
 (0)