M .gitignore => .gitignore +1 -0
@@ 1,3 1,4 @@
/target
*~
/stamps_cache
+/stamp_packs
M Cargo.lock => Cargo.lock +316 -11
@@ 34,7 34,7 @@ dependencies = [
"accesskit_consumer",
"atspi-common",
"serde",
- "thiserror",
+ "thiserror 1.0.69",
"zvariant",
]
@@ 126,6 126,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
+name = "aes"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+]
+
+[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 156,7 167,7 @@ dependencies = [
"ndk-context",
"ndk-sys 0.6.0+11769913",
"num_enum",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ 175,6 186,15 @@ dependencies = [
]
[[package]]
+name = "arbitrary"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
+dependencies = [
+ "derive_arbitrary",
+]
+
+[[package]]
name = "arboard"
version = "3.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 552,6 572,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
[[package]]
+name = "bzip2"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
+dependencies = [
+ "bzip2-sys",
+ "libc",
+]
+
+[[package]]
+name = "bzip2-sys"
+version = "0.1.11+1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
name = "calloop"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 562,7 603,7 @@ dependencies = [
"polling",
"rustix",
"slab",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ 622,6 663,16 @@ dependencies = [
]
[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+]
+
+[[package]]
name = "clipboard-win"
version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 660,6 711,12 @@ dependencies = [
]
[[package]]
+name = "constant_time_eq"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
+
+[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 719,6 776,21 @@ dependencies = [
]
[[package]]
+name = "crc"
+version = "3.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
+dependencies = [
+ "crc-catalog",
+]
+
+[[package]]
+name = "crc-catalog"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
+
+[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 750,6 822,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
[[package]]
+name = "deflate64"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b"
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "derive_arbitrary"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 757,6 855,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
+ "subtle",
]
[[package]]
@@ 880,7 979,7 @@ dependencies = [
"epaint",
"log",
"profiling",
- "thiserror",
+ "thiserror 1.0.69",
"type-map",
"web-time",
"wgpu",
@@ 1465,6 1564,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest",
+]
+
+[[package]]
name = "home"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 1756,6 1864,15 @@ dependencies = [
]
[[package]]
+name = "inout"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
name = "ipnet"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 1778,7 1895,7 @@ dependencies = [
"combine",
"jni-sys",
"log",
- "thiserror",
+ "thiserror 1.0.69",
"walkdir",
"windows-sys 0.45.0",
]
@@ 1881,12 1998,28 @@ dependencies = [
]
[[package]]
+name = "lockfree-object-pool"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e"
+
+[[package]]
name = "log"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
+name = "lzma-rs"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
+dependencies = [
+ "byteorder",
+ "crc",
+]
+
+[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 1988,7 2121,7 @@ dependencies = [
"rustc-hash",
"spirv",
"termcolor",
- "thiserror",
+ "thiserror 1.0.69",
"unicode-xid",
]
@@ 2021,7 2154,7 @@ dependencies = [
"ndk-sys 0.6.0+11769913",
"num_enum",
"raw-window-handle",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ 2068,6 2201,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 2432,6 2571,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
+name = "pbkdf2"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
+dependencies = [
+ "digest",
+ "hmac",
+]
+
+[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 2490,7 2639,9 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
+ "sha256",
"strum",
+ "zip",
]
[[package]]
@@ 2528,6 2679,12 @@ dependencies = [
]
[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
name = "ppv-lite86"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 2917,6 3074,30 @@ dependencies = [
]
[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sha256"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "hex",
+ "sha2",
+ "tokio",
+]
+
+[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 2975,7 3156,7 @@ dependencies = [
"log",
"memmap2",
"rustix",
- "thiserror",
+ "thiserror 1.0.69",
"wayland-backend",
"wayland-client",
"wayland-csd-frame",
@@ 3158,7 3339,16 @@ version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
- "thiserror-impl",
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
+dependencies = [
+ "thiserror-impl 2.0.9",
]
[[package]]
@@ 3173,6 3363,36 @@ dependencies = [
]
[[package]]
+name = "thiserror-impl"
+version = "2.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "time"
+version = "0.3.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
+dependencies = [
+ "deranged",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
name = "tiny-skia"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 3722,7 3942,7 @@ dependencies = [
"raw-window-handle",
"rustc-hash",
"smallvec",
- "thiserror",
+ "thiserror 1.0.69",
"wgpu-hal",
"wgpu-types",
]
@@ 3760,7 3980,7 @@ dependencies = [
"renderdoc-sys",
"rustc-hash",
"smallvec",
- "thiserror",
+ "thiserror 1.0.69",
"wasm-bindgen",
"web-sys",
"wgpu-types",
@@ 4405,6 4625,20 @@ name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
[[package]]
name = "zerovec"
@@ 4429,6 4663,77 @@ dependencies = [
]
[[package]]
+name = "zip"
+version = "2.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45"
+dependencies = [
+ "aes",
+ "arbitrary",
+ "bzip2",
+ "constant_time_eq",
+ "crc32fast",
+ "crossbeam-utils",
+ "deflate64",
+ "displaydoc",
+ "flate2",
+ "hmac",
+ "indexmap",
+ "lzma-rs",
+ "memchr",
+ "pbkdf2",
+ "rand",
+ "sha1",
+ "thiserror 2.0.9",
+ "time",
+ "zeroize",
+ "zopfli",
+ "zstd",
+]
+
+[[package]]
+name = "zopfli"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946"
+dependencies = [
+ "bumpalo",
+ "crc32fast",
+ "lockfree-object-pool",
+ "log",
+ "once_cell",
+ "simd-adler32",
+]
+
+[[package]]
+name = "zstd"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "7.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059"
+dependencies = [
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.13+zstd.1.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
+
+[[package]]
name = "zvariant"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
M Cargo.toml => Cargo.toml +2 -0
@@ 10,4 10,6 @@ egui_extras = { version = "0.30.0", features = ["image", "file"] }
reqwest = { version = "0.12.12", features = ["blocking"] }
serde = { version = "1.0.217", features = ["derive"] }
serde_json = "1.0.134"
+sha256 = "1.5.0"
strum = { version = "0.26.3", features = ["derive"] }
+zip = "2.2.2"
D slugs_en.json => slugs_en.json +0 -11
@@ 1,11 0,0 @@
-{
- "701": "nene_i_dont_want_to_lose",
- "1": "miku_hello",
- "64": "honami_oh",
- "480": "text_nice_to_meet_you",
- "574": "ena_can_i_keep_drawing",
- "437": "text_have_some_bonus_energy",
- "505": "minori_shocked",
- "250": "mizuki_laugh",
- "723": "mizuki_satisfied"
-}>
\ No newline at end of file
M src/en.rs => src/en.rs +13 -251
@@ 1,256 1,18 @@
-use std::collections::HashSet;
-
-use serde::Deserialize;
-use strum::{EnumIter, IntoEnumIterator, EnumProperty};
-
-#[derive(Clone, Debug, Default, Deserialize)]
-#[serde(rename_all = "camelCase", deny_unknown_fields)]
-pub struct RawStamp {
- id: usize,
- seq: usize,
-
- name: String,
- description: Option<String>,
- stamp_type: String,
-
- assetbundle_name: String,
- balloon_assetbundle_name: String,
-
- // ignored by us, used in sekai-viewer UI
- archive_published_at: usize,
- // ignored by us, used in sekai-viewer UI
- archive_display_type: Option<String>,
-
- character_id_1: Option<Character>,
- character_id_2: Option<Character>,
- game_character_unit_id: Option<Character>
-}
-
-impl RawStamp {
- pub fn to_stamp(self) -> Stamp {
- use StampCharacter::*;
-
- let RawStamp { id, seq,
- name, description, stamp_type,
- assetbundle_name, balloon_assetbundle_name,
- character_id_1, character_id_2, game_character_unit_id, .. } = self;
-
- let character = match (character_id_1, character_id_2, game_character_unit_id) {
- (None, None, None)
- => Nobody,
- (Some(character_id), None, None)
- => Solo{character_id},
- (Some(character_id_1), Some(character_id_2), None)
- => Duo{character_id_1, character_id_2},
- (Some(character_id), None, Some(game_character_unit_id))
- => SoloExtra{character_id, game_character_unit_id},
- (None, None, Some(game_character_unit_id))
- => GCUID{game_character_unit_id},
-
- x => { panic!("They added a new character format: {x:?}"); }
- };
-
- Stamp { id, seq,
- name, description, stamp_type,
- assetbundle_name, balloon_assetbundle_name,
- character }
- }
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct Stamp {
- pub id: usize,
- pub seq: usize,
-
- pub name: String,
- pub description: Option<String>,
- pub stamp_type: String,
-
- pub assetbundle_name: String,
- pub balloon_assetbundle_name: String,
-
- pub character: StampCharacter
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Hash)]
-pub enum StampCharacter {
- #[default]
- Nobody,
- Solo{character_id: Character},
- Duo{character_id_1: Character, character_id_2: Character},
- SoloExtra{character_id: Character, game_character_unit_id: Character},
- GCUID{game_character_unit_id: Character},
-}
-impl std::fmt::Display for StampCharacter {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match *self {
- Self::Nobody => write!(f, "text"),
- Self::Solo{character_id} => write!(f, "{}", character_id.get_str("prefix").unwrap()),
- Self::Duo{character_id_1, character_id_2} => write!(f, "{}_{}", character_id_1.get_str("prefix").unwrap(), character_id_2.get_str("prefix").unwrap()),
- Self::SoloExtra{game_character_unit_id, ..} => write!(f, "{}", game_character_unit_id.get_str("prefix").unwrap()),
- Self::GCUID{game_character_unit_id} => write!(f, "text_{}", game_character_unit_id.get_str("prefix").unwrap())
- }
- }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Hash, EnumIter, EnumProperty)]
-#[repr(u8)]
-#[serde(from = "u8")]
-pub enum Character {
- // Leo / Need
- #[strum(props(prefix = "ichika"))]
- Ichika = 1,
- #[strum(props(prefix = "saki"))]
- Saki = 2,
- #[strum(props(prefix = "honami"))]
- Honami = 3,
- #[strum(props(prefix = "shiho"))]
- Shiho = 4,
-
- // MORE MORE JUMP!
- #[strum(props(prefix = "minori"))]
- Minori = 5,
- #[strum(props(prefix = "haruka"))]
- Haruka = 6,
- #[strum(props(prefix = "airi"))]
- Airi = 7,
- #[strum(props(prefix = "shizuku"))]
- Shizuku = 8,
-
- // Vivid BAD SQUAD
- #[strum(props(prefix = "kohane"))]
- Kohane = 9,
- #[strum(props(prefix = "an"))]
- An = 10,
- #[strum(props(prefix = "akito"))]
- Akito = 11,
- #[strum(props(prefix = "toya"))]
- Toya = 12,
-
- // Wonderlands x Showtime
- #[strum(props(prefix = "tsukasa"))]
- Tsukasa = 13,
- #[strum(props(prefix = "emu"))]
- Emu = 14,
- #[strum(props(prefix = "nene"))]
- Nene = 15,
- #[strum(props(prefix = "rui"))]
- Rui = 16,
-
- // Nightcord at 25:00
- #[strum(props(prefix = "kanade"))]
- Kanade = 17,
- #[strum(props(prefix = "mafuyu"))]
- Mafuyu = 18,
- #[strum(props(prefix = "ena"))]
- Ena = 19,
- #[strum(props(prefix = "mizuki"))]
- Mizuki = 20,
-
- // VOCALOID
- #[strum(props(prefix = "miku"))]
- Miku = 21,
- #[strum(props(prefix = "rin"))]
- Rin = 22,
- #[strum(props(prefix = "len"))]
- Len = 23,
- #[strum(props(prefix = "luka"))]
- Luka = 24,
- #[strum(props(prefix = "meiko"))]
- MEIKO = 25,
- #[strum(props(prefix = "kaito"))]
- KAITO = 26,
-
- // "Game Character Unit ID"
- // Basically just, character IDs associated to a particular group
- // - Miku
- #[strum(props(prefix = "ln_miku"))]
- LnMiku = 27,
- #[strum(props(prefix = "mmj_miku"))]
- MmjMiku = 28,
- #[strum(props(prefix = "vbs_miku"))]
- VbsMiku = 29,
- #[strum(props(prefix = "wxs_miku"))]
- WxsMiku = 30,
- #[strum(props(prefix = "n25_miku"))]
- N25Miku = 31,
-
- // - Rin
- #[strum(props(prefix = "ln_rin"))]
- LnRin = 32,
- #[strum(props(prefix = "mmj_rin"))]
- MmjRin = 33,
- #[strum(props(prefix = "vbs_rin"))]
- VbsRin = 34,
- #[strum(props(prefix = "wxs_rin"))]
- WxsRin = 35,
- #[strum(props(prefix = "n25_rin"))]
- N25Rin = 36,
-
- // - Len
- #[strum(props(prefix = "ln_len"))]
- LnLen = 37,
- #[strum(props(prefix = "mmj_len"))]
- MmjLen = 38,
- #[strum(props(prefix = "vbs_len"))]
- VbsLen = 39,
- #[strum(props(prefix = "wxs_len"))]
- WxsLen = 40,
- #[strum(props(prefix = "n25_len"))]
- N25Len = 41,
-
- // - Luka
- #[strum(props(prefix = "ln_luka"))]
- LnLuka = 42,
- #[strum(props(prefix = "mmj_luka"))]
- MmjLuka = 43,
- #[strum(props(prefix = "vbs_luka"))]
- VbsLuka = 44,
- #[strum(props(prefix = "wxs_luka"))]
- WxsLuka = 45,
- #[strum(props(prefix = "n25_luka"))]
- N25Luka = 46,
-
- // - MEIKO
- #[strum(props(prefix = "ln_meiko"))]
- LnMEIKO = 47,
- #[strum(props(prefix = "mmj_meiko"))]
- MmjMEIKO = 48,
- #[strum(props(prefix = "vbs_meiko"))]
- VbsMEIKO = 49,
- #[strum(props(prefix = "wxs_meiko"))]
- WxsMEIKO = 50,
- #[strum(props(prefix = "n25_meiko"))]
- N25MEIKO = 51,
-
- // - KAITO
- #[strum(props(prefix = "ln_kaito"))]
- LnKAITO = 52,
- #[strum(props(prefix = "mmj_kaito"))]
- MmjKAITO = 53,
- #[strum(props(prefix = "vbs_kaito"))]
- VbsKAITO = 54,
- #[strum(props(prefix = "wxs_kaito"))]
- WxsKAITO = 55,
- #[strum(props(prefix = "n25_kaito"))]
- N25KAITO = 56,
-}
-impl From<u8> for Character {
- fn from(val: u8) -> Self {
- for var in Self::iter() {
- if var as u8 == val {
- return var;
+pub struct Helpers;
+impl crate::Helpers for Helpers {
+ fn escape_name(name: &str) -> String {
+ let mut ret = String::new();
+
+ let name = name.split(":").nth(1).unwrap().trim();
+
+ for c in name.chars() {
+ if c.is_ascii_alphanumeric() {
+ ret.push(c.to_ascii_lowercase());
+ } else if c.is_ascii_whitespace() {
+ ret.push('_');
}
}
- panic!("Unknown Character ID: {val}");
- }
-}
-impl std::fmt::Display for Character {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- write!(f, "{self:?}")
+ ret
}
}
-
-impl Stamp {
-}
M src/main.rs => src/main.rs +102 -40
@@ 1,13 1,35 @@
#![feature(iter_intersperse)]
mod en;
+
+mod model;
mod slug;
-use std::collections::HashSet;
+use std::collections::{BTreeMap, HashSet};
use std::fs::{File, self};
-use std::path::PathBuf;
+use std::io::Write;
+use std::path::{Path, PathBuf};
use bytes::Bytes;
use reqwest::blocking::Client;
+use strum::{EnumIs, EnumProperty};
+use zip::write::{SimpleFileOptions, ZipWriter};
+
+const BASE_DOWNLOAD_PATH: &'static str = "https://aleteoryx.me/downloads2/emotes/pleroma/";
+
+trait Helpers {
+ fn escape_name(name: &str) -> String;
+}
+
+#[derive(Default, serde::Serialize)]
+struct PackMetadata {
+ name: String,
+ description: String,
+ homepage: String,
+ files: String,
+ src: String,
+ src_sha256: String,
+ license: String
+}
fn get_stamp_asset_png(client: &Client, id: &str) -> Bytes {
client
@@ 22,25 44,28 @@ fn get_stamp_asset_png(client: &Client, id: &str) -> Bytes {
fn main() {
let repo_dir: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_owned()).into();
-
let client = Client::new();
// English region (global)
- let sekai_en_dir = repo_dir.join("sekai_world/db_en_diff");
- let stamps_en_path = sekai_en_dir.join("stamps.json");
- let stamps_en =
- serde_json::from_reader::<_, Vec<en::RawStamp>>(
- File::open(&stamps_en_path)
+ handle_region::<en::Helpers>(&client, &repo_dir, "en");
+}
+
+fn handle_region<H: Helpers>(client: &Client, repo_dir: &Path, region: &'static str) {
+ let sekai_dir = repo_dir.join(format!("sekai_world/db_{region}_diff"));
+
+ let stamps =
+ serde_json::from_reader::<_, Vec<model::RawStamp>>(
+ File::open(sekai_dir.join("stamps.json"))
.expect("Couldn't open stamps.json"))
.expect("stamps.json invalid")
.into_iter()
.map(|a| a.to_stamp())
.collect::<Vec<_>>();
- let cache_dir = repo_dir.join("stamps_cache/en");
+ let cache_dir = repo_dir.join("stamps_cache").join(®ion);
fs::create_dir_all(&cache_dir);
- for stamp in &stamps_en {
+ for stamp in &stamps {
let download_path = cache_dir.join(&stamp.assetbundle_name).with_extension("png");
if !fs::exists(&download_path).unwrap() {
println!("Downlading asset {}...", &stamp.assetbundle_name);
@@ 50,44 75,81 @@ fn main() {
}
let character_stamps =
- stamps_en.clone().into_iter()
- .filter(|a|
- match a.character {
- en::StampCharacter::Solo { .. } | en::StampCharacter::SoloExtra { .. } => true,
- _ => false
- })
+ stamps.clone().into_iter()
+ .filter(|a| a.character.is_solo() || a.character.is_solo_extra())
.collect::<Vec<_>>();
let text_stamps =
- stamps_en.clone().into_iter()
- .filter(|a|
- match a.character {
- en::StampCharacter::Nobody => true,
- _ => false
- })
+ stamps.clone().into_iter()
+ .filter(|a| a.character.is_nobody())
.collect::<Vec<_>>();
-
- for stamp in text_stamps {
- println!("{} => {}", stamp.name, stamp.name.to_ascii_lowercase().replace(" ", "_"));
- }
-
let name_stamps =
- stamps_en.clone().into_iter()
- .filter(|a|
- match a.character {
- en::StampCharacter::GCUID { .. } => true,
- _ => false
- })
+ stamps.clone().into_iter()
+ .filter(|a| a.character.is_gcuid())
.collect::<Vec<_>>();
let pair_stamps =
- stamps_en.clone().into_iter()
- .filter(|a|
- match a.character {
- en::StampCharacter::Duo { .. } => true,
- _ => false
- })
+ stamps.clone().into_iter()
+ .filter(|a| a.character.is_duo())
.collect::<Vec<_>>();
- slug::SlugPrompter::run("en".into(), &repo_dir, character_stamps);
+ let packs_dir = repo_dir.join("stamp_packs");
+ let out_dir = packs_dir.join("Project SEKAI").join(region);
+ fs::create_dir_all(&out_dir);
+ let mut packs_list: BTreeMap<String, PackMetadata> = BTreeMap::new();
+
+ let character_out_dir = out_dir.join("characters");
+ fs::create_dir_all(&character_out_dir);
+
+ let options = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Deflated);
+ for cid in 1..=26 {
+ let character = model::Character::from(cid);
+ let character_slug = character.get_str("prefix").unwrap();
+ let zip_path = character_out_dir.join(&character_slug).with_extension("zip");
+ let mut writer =
+ ZipWriter::new(
+ File::create(&zip_path).unwrap());
+ let mut emotes: BTreeMap<String, String> = BTreeMap::new();
+
+ println!("Generating character stamp pack for {}...", character);
+
+ for stamp in &character_stamps {
+ match stamp.character {
+ model::StampCharacter::Solo{ character_id } |
+ model::StampCharacter::SoloExtra{ character_id, .. } if character_id as u8 == cid => {
+ let character_slug = stamp.character.to_string() + "_" + &H::escape_name(&stamp.name);
+ writer.start_file(character_slug.clone() + ".png", options);
+ writer.write_all(&fs::read(cache_dir.join(&stamp.assetbundle_name).with_extension("png")).unwrap());
+ emotes.insert(character_slug.clone(), character_slug + ".png");
+ },
+ _ => ()
+ }
+ }
+
+ writer.finish();
+
+ File::create(
+ character_out_dir
+ .join(&character_slug)
+ .with_extension("json"))
+ .unwrap()
+ .write_all(&serde_json::to_vec(&emotes).unwrap());
+
+ let pack_path = format!("Project SEKAI/{region}/characters/{character_slug}");
+
+ packs_list.insert(
+ pack_path.clone(),
+ PackMetadata {
+ name: format!("Project SEKAI ({region}): {}", character.get_str(&format!("name_{region}")).unwrap()),
+ description: "Made possible by the sekai-viewer project. <https:///sekai.best>".into(),
+ homepage: "https://git.amehut.dev/~aleteoryx/pjsekai_emote_packs".into(),
+ files: BASE_DOWNLOAD_PATH.to_string() + &pack_path + ".json",
+ src: BASE_DOWNLOAD_PATH.to_string() + &pack_path + ".zip",
+ src_sha256: sha256::try_digest(&zip_path).unwrap(),
+ license: "N/A".into()
+ });
+ }
+ File::create(packs_dir.join("manifest.json"))
+ .unwrap()
+ .write_all(&serde_json::to_vec_pretty(&packs_list).unwrap());
}
A src/model.rs => src/model.rs +256 -0
@@ 0,0 1,256 @@
+use std::collections::HashSet;
+
+use serde::Deserialize;
+use strum::{EnumIter, IntoEnumIterator, EnumProperty, EnumIs};
+
+#[derive(Clone, Debug, Default, Deserialize)]
+#[serde(rename_all = "camelCase", deny_unknown_fields)]
+pub struct RawStamp {
+ id: usize,
+ seq: usize,
+
+ name: String,
+ description: Option<String>,
+ stamp_type: String,
+
+ assetbundle_name: String,
+ balloon_assetbundle_name: String,
+
+ // ignored by us, used in sekai-viewer UI
+ archive_published_at: usize,
+ // ignored by us, used in sekai-viewer UI
+ archive_display_type: Option<String>,
+
+ character_id_1: Option<Character>,
+ character_id_2: Option<Character>,
+ game_character_unit_id: Option<Character>
+}
+
+impl RawStamp {
+ pub fn to_stamp(self) -> Stamp {
+ use StampCharacter::*;
+
+ let RawStamp { id, seq,
+ name, description, stamp_type,
+ assetbundle_name, balloon_assetbundle_name,
+ character_id_1, character_id_2, game_character_unit_id, .. } = self;
+
+ let character = match (character_id_1, character_id_2, game_character_unit_id) {
+ (None, None, None)
+ => Nobody,
+ (Some(character_id), None, None)
+ => Solo{character_id},
+ (Some(character_id_1), Some(character_id_2), None)
+ => Duo{character_id_1, character_id_2},
+ (Some(character_id), None, Some(game_character_unit_id))
+ => SoloExtra{character_id, game_character_unit_id},
+ (None, None, Some(game_character_unit_id))
+ => GCUID{game_character_unit_id},
+
+ x => { panic!("They added a new character format: {x:?}"); }
+ };
+
+ Stamp { id, seq,
+ name, description, stamp_type,
+ assetbundle_name, balloon_assetbundle_name,
+ character }
+ }
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct Stamp {
+ pub id: usize,
+ pub seq: usize,
+
+ pub name: String,
+ pub description: Option<String>,
+ pub stamp_type: String,
+
+ pub assetbundle_name: String,
+ pub balloon_assetbundle_name: String,
+
+ pub character: StampCharacter
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Hash, EnumIs)]
+pub enum StampCharacter {
+ #[default]
+ Nobody,
+ Solo{character_id: Character},
+ Duo{character_id_1: Character, character_id_2: Character},
+ SoloExtra{character_id: Character, game_character_unit_id: Character},
+ GCUID{game_character_unit_id: Character},
+}
+impl std::fmt::Display for StampCharacter {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match *self {
+ Self::Nobody => write!(f, "text"),
+ Self::Solo{character_id} => write!(f, "{}", character_id.get_str("prefix").unwrap()),
+ Self::Duo{character_id_1, character_id_2} => write!(f, "{}_{}", character_id_1.get_str("prefix").unwrap(), character_id_2.get_str("prefix").unwrap()),
+ Self::SoloExtra{game_character_unit_id, ..} => write!(f, "{}", game_character_unit_id.get_str("prefix").unwrap()),
+ Self::GCUID{game_character_unit_id} => write!(f, "text_{}", game_character_unit_id.get_str("prefix").unwrap())
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Hash, EnumIter, EnumProperty)]
+#[repr(u8)]
+#[serde(from = "u8")]
+pub enum Character {
+ // Leo / Need
+ #[strum(props(prefix = "ichika", name_en = "Ichika Hoshino"))]
+ Ichika = 1,
+ #[strum(props(prefix = "saki", name_en = "Saki Tenma"))]
+ Saki = 2,
+ #[strum(props(prefix = "honami", name_en = "Honami Mochizuki"))]
+ Honami = 3,
+ #[strum(props(prefix = "shiho", name_en = "Shiho Hinomori"))]
+ Shiho = 4,
+
+ // MORE MORE JUMP!
+ #[strum(props(prefix = "minori", name_en = "Minori Hanasato"))]
+ Minori = 5,
+ #[strum(props(prefix = "haruka", name_en = "Haruka Kiritani"))]
+ Haruka = 6,
+ #[strum(props(prefix = "airi", name_en = "Airi Momoi"))]
+ Airi = 7,
+ #[strum(props(prefix = "shizuku", name_en = "Shizuku Hinomori"))]
+ Shizuku = 8,
+
+ // Vivid BAD SQUAD
+ #[strum(props(prefix = "kohane", name_en = "Kohane Azusawa"))]
+ Kohane = 9,
+ #[strum(props(prefix = "an", name_en = "An Shiraishi"))]
+ An = 10,
+ #[strum(props(prefix = "akito", name_en = "Akito Shinonome"))]
+ Akito = 11,
+ #[strum(props(prefix = "toya", name_en = "Toya Aoyagi"))]
+ Toya = 12,
+
+ // Wonderlands x Showtime
+ #[strum(props(prefix = "tsukasa", name_en = "Tsukasa Tenma"))]
+ Tsukasa = 13,
+ #[strum(props(prefix = "emu", name_en = "Emu Otori"))]
+ Emu = 14,
+ #[strum(props(prefix = "nene", name_en = "Nene Kusanagi"))]
+ Nene = 15,
+ #[strum(props(prefix = "rui", name_en = "Rui Kamishiro"))]
+ Rui = 16,
+
+ // Nightcord at 25:00
+ #[strum(props(prefix = "kanade", name_en = "Kanade Yoisaki"))]
+ Kanade = 17,
+ #[strum(props(prefix = "mafuyu", name_en = "Mafuyu Asahina"))]
+ Mafuyu = 18,
+ #[strum(props(prefix = "ena", name_en = "Ena Shinonome"))]
+ Ena = 19,
+ #[strum(props(prefix = "mizuki", name_en = "Mizuki Akiyama"))]
+ Mizuki = 20,
+
+ // VOCALOID
+ #[strum(props(prefix = "miku", name_en = "Hatsune Miku"))]
+ Miku = 21,
+ #[strum(props(prefix = "rin", name_en = "Kagamine Rin"))]
+ Rin = 22,
+ #[strum(props(prefix = "len", name_en = "Kagamine Len"))]
+ Len = 23,
+ #[strum(props(prefix = "luka", name_en = "Megurine Luka"))]
+ Luka = 24,
+ #[strum(props(prefix = "meiko", name_en = "MEIKO"))]
+ MEIKO = 25,
+ #[strum(props(prefix = "kaito", name_en = "KAITO"))]
+ KAITO = 26,
+
+ // "Game Character Unit ID"
+ // Basically just, character IDs associated to a particular group
+ // - Miku
+ #[strum(props(prefix = "ln_miku", name_en = "Leo / Need Miku"))]
+ LnMiku = 27,
+ #[strum(props(prefix = "mmj_miku", name_en = "MORE MORE JUMP! Miku"))]
+ MmjMiku = 28,
+ #[strum(props(prefix = "vbs_miku", name_en = "Vivid BAD SQUAD Miku"))]
+ VbsMiku = 29,
+ #[strum(props(prefix = "wxs_miku", name_en = "Wonderlands x Showtime Miku"))]
+ WxsMiku = 30,
+ #[strum(props(prefix = "n25_miku", name_en = "Nightcord at 25:00 Miku"))]
+ N25Miku = 31,
+
+ // - Rin
+ #[strum(props(prefix = "ln_rin", name_en = "Leo / Need Rin"))]
+ LnRin = 32,
+ #[strum(props(prefix = "mmj_rin", name_en = "MORE MORE JUMP! Rin"))]
+ MmjRin = 33,
+ #[strum(props(prefix = "vbs_rin", name_en = "Vivid BAD SQUAD Rin"))]
+ VbsRin = 34,
+ #[strum(props(prefix = "wxs_rin", name_en = "Wonderlands x Showtime Rin"))]
+ WxsRin = 35,
+ #[strum(props(prefix = "n25_rin", name_en = "Nightcord at 25:00 Rin"))]
+ N25Rin = 36,
+
+ // - Len
+ #[strum(props(prefix = "ln_len", name_en = "Leo / Need Len"))]
+ LnLen = 37,
+ #[strum(props(prefix = "mmj_len", name_en = "MORE MORE JUMP! Len"))]
+ MmjLen = 38,
+ #[strum(props(prefix = "vbs_len", name_en = "Vivid BAD SQUAD Len"))]
+ VbsLen = 39,
+ #[strum(props(prefix = "wxs_len", name_en = "Wonderlands x Showtime Len"))]
+ WxsLen = 40,
+ #[strum(props(prefix = "n25_len", name_en = "Nightcord at 25:00 Len"))]
+ N25Len = 41,
+
+ // - Luka
+ #[strum(props(prefix = "ln_luka", name_en = "Leo / Need Luka"))]
+ LnLuka = 42,
+ #[strum(props(prefix = "mmj_luka", name_en = "MORE MORE JUMP! Luka"))]
+ MmjLuka = 43,
+ #[strum(props(prefix = "vbs_luka", name_en = "Vivid BAD SQUAD Luka"))]
+ VbsLuka = 44,
+ #[strum(props(prefix = "wxs_luka", name_en = "Wonderlands x Showtime Luka"))]
+ WxsLuka = 45,
+ #[strum(props(prefix = "n25_luka", name_en = "Nightcord at 25:00 Luka"))]
+ N25Luka = 46,
+
+ // - MEIKO
+ #[strum(props(prefix = "ln_meiko", name_en = "Leo / Need MEIKO"))]
+ LnMEIKO = 47,
+ #[strum(props(prefix = "mmj_meiko", name_en = "MORE MORE JUMP! MEIKO"))]
+ MmjMEIKO = 48,
+ #[strum(props(prefix = "vbs_meiko", name_en = "Vivid BAD SQUAD MEIKO"))]
+ VbsMEIKO = 49,
+ #[strum(props(prefix = "wxs_meiko", name_en = "Wonderlands x Showtime MEIKO"))]
+ WxsMEIKO = 50,
+ #[strum(props(prefix = "n25_meiko", name_en = "Nightcord at 25:00 MEIKO"))]
+ N25MEIKO = 51,
+
+ // - KAITO
+ #[strum(props(prefix = "ln_kaito", name_en = "Leo / Need KAITO"))]
+ LnKAITO = 52,
+ #[strum(props(prefix = "mmj_kaito", name_en = "MORE MORE JUMP! KAITO"))]
+ MmjKAITO = 53,
+ #[strum(props(prefix = "vbs_kaito", name_en = "Vivid BAD SQUAD KAITO"))]
+ VbsKAITO = 54,
+ #[strum(props(prefix = "wxs_kaito", name_en = "Wonderlands x Showtime KAITO"))]
+ WxsKAITO = 55,
+ #[strum(props(prefix = "n25_kaito", name_en = "Nightcord at 25:00 KAITO"))]
+ N25KAITO = 56,
+}
+impl From<u8> for Character {
+ fn from(val: u8) -> Self {
+ for var in Self::iter() {
+ if var as u8 == val {
+ return var;
+ }
+ }
+
+ panic!("Unknown Character ID: {val}");
+ }
+}
+impl std::fmt::Display for Character {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+ write!(f, "{self:?}")
+ }
+}
+
+impl Stamp {
+}
M src/slug.rs => src/slug.rs +17 -11
@@ 1,4 1,4 @@
-use std::collections::HashMap;
+use std::collections::BTreeMap;
use std::fs::{File, self};
use std::path::{Path, PathBuf};
@@ 6,9 6,9 @@ use eframe::egui;
#[derive(Default)]
pub struct SlugPrompter {
- id_to_stamp_map: HashMap<usize, crate::en::Stamp>,
- id_to_slug_map: HashMap<usize, Option<String>>,
- slug_to_id_map: HashMap<String, usize>,
+ id_to_stamp_map: BTreeMap<usize, crate::model::Stamp>,
+ id_to_slug_map: BTreeMap<usize, Option<String>>,
+ slug_to_id_map: BTreeMap<String, usize>,
current_id: usize,
current_slug: String,
@@ 20,9 20,9 @@ pub struct SlugPrompter {
}
impl SlugPrompter {
- pub fn run(locale: String, repo_dir: &Path, stamps: Vec<crate::en::Stamp>) {
+ pub fn run(locale: String, repo_dir: &Path, stamps: Vec<crate::model::Stamp>) {
let file_path = repo_dir.join(format!("slugs_{locale}.json"));
- let id_to_slug_map: HashMap<usize, Option<String>>;
+ let id_to_slug_map: BTreeMap<usize, Option<String>>;
if fs::exists(&file_path).unwrap() {
id_to_slug_map =
serde_json::from_reader(
@@ 40,7 40,7 @@ impl SlugPrompter {
Box::new(|cc| Ok(Box::new(SlugPrompter::new(cc, id_to_slug_map, stamps, repo_dir.join("stamps_cache").join(&locale), file_path)))));
}
- fn new(_cc: &eframe::CreationContext<'_>, id_to_slug_map: HashMap<usize, Option<String>>, stamps: Vec<crate::en::Stamp>, cache_dir: PathBuf, file_path: PathBuf) -> Self {
+ fn new(_cc: &eframe::CreationContext<'_>, id_to_slug_map: BTreeMap<usize, Option<String>>, stamps: Vec<crate::model::Stamp>, cache_dir: PathBuf, file_path: PathBuf) -> Self {
let current_id = stamps[0].id;
let slug_to_id_map = id_to_slug_map.clone().into_iter().filter_map(|(a, b)| b.map(|b2| (b2, a))).collect();
@@ 97,18 97,24 @@ impl eframe::App for SlugPrompter {
.split("]").nth(1).unwrap()
.trim());
});
- ui.text_edit_singleline(&mut self.current_slug);
+ let text_res = ui.text_edit_singleline(&mut self.current_slug);
+
+ ui.label(format!("{}/{}", self.id_to_slug_map.len(), self.id_to_stamp_map.len()));
if let Some(s) = self.current_err.as_ref() {
ui.colored_label(egui::Color32::RED, s);
}
- if ui.button("Confirm").clicked() {
+ if ui.button("Confirm").clicked() || text_res.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
if self.slug_to_id_map.contains_key(&self.current_slug) {
self.current_err = Some(format!("Slug \"{}\" already taken by {}!", self.current_slug, self.slug_to_id_map[&self.current_slug]));
} else {
- self.slug_to_id_map.insert(self.current_slug.clone(), self.current_id);
- self.id_to_slug_map.insert(self.current_id, Some(self.current_slug.clone()));
+ if self.current_slug == "" {
+ self.id_to_slug_map.insert(self.current_id, None);
+ } else {
+ self.slug_to_id_map.insert(self.current_slug.clone(), self.current_id);
+ self.id_to_slug_map.insert(self.current_id, Some(self.current_slug.clone()));
+ }
self.write_file();
}