Skip to main content

wow_sharedmedia/
lib.rs

1//! # wow-sharedmedia
2//!
3//! A Rust library for building World of Warcraft addons that register media
4//! assets — fonts, textures, sounds, borders, and statusbars — with
5//! [LibSharedMedia-3.0][lsm].
6//!
7//! ## Overview
8//!
9//! `wow-sharedmedia` provides stateless, one-shot operations for:
10//!
11//! - **Importing** media files (PNG, TGA, WebP, JPEG, BLP, TTF, OTF, OGG, MP3, WAV)
12//!   with automatic format conversion to WoW-compatible formats.
13//! - **Removing** media entries and their associated files.
14//! - **Updating** entry metadata (display key, tags, locale masks).
15//! - **Reading** the addon's `data.lua` file into a typed Rust struct.
16//!
17//! Each operation is atomic: read `data.lua` → modify → write `data.lua`.
18//! No in-memory state, no dirty tracking, no separate save/generate/deploy steps.
19//!
20//! ## Installation
21//!
22//! ```toml
23//! [dependencies]
24//! wow-sharedmedia = "0.2"
25//! ```
26//!
27//! ## Quick Start
28//!
29//! ```no_run
30//! use wow_sharedmedia::{ensure_addon_dir, import_media, read_data, ImportOptions, MediaType, DEFAULT_MAX_BACKUPS};
31//! use std::path::Path;
32//!
33//! fn main() -> Result<(), wow_sharedmedia::Error> {
34//!
35//! // Initialize addon directory (creates data.lua, loader.lua, .toc, media/ subdirs)
36//! let addon_dir = Path::new("AddOns/MyMedia");
37//! ensure_addon_dir(addon_dir, DEFAULT_MAX_BACKUPS)?;
38//!
39//! // Import a statusbar texture
40//! let source = Path::new("assets/my-statusbar.png");
41//! let opts = ImportOptions::new(MediaType::Statusbar, "My Bar", &source);
42//! let result = import_media(addon_dir, opts, DEFAULT_MAX_BACKUPS)?;
43//! println!("Imported: {} (ID: {})", result.entry.key, result.entry.id);
44//!
45//! // Read all entries
46//! let data = read_data(addon_dir)?;
47//! for entry in &data.entries {
48//!     println!("  {} [{}] → {}", entry.key, entry.media_type, entry.file);
49//! }
50//!
51//! Ok(())
52//! }
53//! ```
54//!
55//! ## Addon Directory Structure
56//!
57//! After `ensure_addon_dir`, the directory layout is driven by the folder
58//! name. For a folder named `MyMedia`:
59//!
60//! ```text
61//! MyMedia/
62//! ├── MyMedia.toc       # WoW addon manifest (auto-generated)
63//! ├── data.lua           # Media registry (Lua table, single source of truth)
64//! ├── loader.lua         # LSM registration script (auto-generated)
65//! ├── libraries/         # Vendored LibSharedMedia-3.0 dependencies
66//! └── media/
67//!     ├── statusbar/    # TGA texture files
68//!     ├── background/   # TGA texture files
69//!     ├── border/       # TGA texture files
70//!     ├── font/         # TTF/OTF font files
71//!     └── sound/        # OGG audio files
72//! ```
73//!
74//! If the folder name starts with `!` (e.g. `!!!MyMedia`), the `.toc`
75//! file will be `!!!MyMedia.toc` but the in-addon title will strip the
76//! leading `!` characters (e.g. `MyMedia`).
77//!
78//! [wow]: https://worldofwarcraft.blizzard.com
79//! [lsm]: https://www.wowace.com/projects/libsharedmedia-3-0/
80
81#![warn(missing_docs)]
82#![deny(unsafe_code)]
83
84pub mod converter;
85mod data;
86mod entry;
87mod error;
88mod lua_io;
89mod media;
90pub mod template;
91
92use std::path::Path;
93
94pub use data::*;
95pub use entry::*;
96pub use error::*;
97pub use media::DEFAULT_MAX_BACKUPS;
98pub use media::*;
99
100/// Extract the addon name from its directory path.
101///
102/// Returns the final component of `addon_dir` as a string slice.
103///
104/// # Panics
105/// Panics if the directory name cannot be determined or is not valid UTF-8.
106pub fn addon_name(addon_dir: &Path) -> &str {
107	addon_dir
108		.file_name()
109		.expect("addon_dir must have a file name")
110		.to_str()
111		.expect("addon_dir name must be valid UTF-8")
112}
113
114/// Derive the human-readable addon title from the addon name.
115///
116/// Strips leading `!` characters. For example, `!!!WindMedia` → `WindMedia`.
117pub fn addon_title(addon_name: &str) -> &str {
118	addon_name.trim_start_matches('!')
119}
120
121#[cfg(test)]
122mod tests {
123	use super::*;
124	use std::path::PathBuf;
125
126	#[test]
127	fn test_addon_name_plain() {
128		let path = PathBuf::from("/wow/AddOns/WindMedia");
129		assert_eq!(addon_name(&path), "WindMedia");
130	}
131
132	#[test]
133	fn test_addon_name_with_bangs() {
134		let path = PathBuf::from("/wow/AddOns/!!!WindMedia");
135		assert_eq!(addon_name(&path), "!!!WindMedia");
136	}
137
138	#[test]
139	fn test_addon_title_strips_bangs() {
140		assert_eq!(addon_title("!!!WindMedia"), "WindMedia");
141	}
142
143	#[test]
144	fn test_addon_title_single_bang() {
145		assert_eq!(addon_title("!TestAddon"), "TestAddon");
146	}
147
148	#[test]
149	fn test_addon_title_no_bang() {
150		assert_eq!(addon_title("WindMedia"), "WindMedia");
151	}
152
153	#[test]
154	fn test_addon_title_all_bangs() {
155		assert_eq!(addon_title("!!!"), "");
156	}
157}