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}