1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/// 智能插件加载器
///
/// 提供自动检测和加载的便捷 API,同时保持底层 API 的灵活性。
use std::path::PathBuf;
use crate::{Plugin, LocalizedPluginContext};
/// 插件加载结果
///
/// 根据插件是否设置 LOCALIZED 标志,自动选择合适的加载方式。
#[derive(Debug)]
pub enum LoadedPlugin {
/// 普通插件(未设置 LOCALIZED 标志)
Standard(Plugin),
/// 本地化插件(设置了 LOCALIZED 标志,已加载 STRING 文件)
Localized(LocalizedPluginContext),
}
impl LoadedPlugin {
/// 智能加载插件
///
/// 自动检测插件类型并选择合适的加载策略:
/// - 如果插件设置了 LOCALIZED 标志 → 加载为 `LocalizedPluginContext`
/// - 如果是普通插件 → 加载为 `Plugin`
///
/// # 参数
/// * `path` - ESP/ESM/ESL 文件路径
/// * `language` - 语言标识(仅对本地化插件有效),默认 "english"
///
/// # 返回
/// 返回 `LoadedPlugin` 枚举,可通过模式匹配处理
///
/// # 示例
/// ```rust,ignore
/// use esp_extractor::LoadedPlugin;
///
/// let loaded = LoadedPlugin::load_auto("MyMod.esp".into(), Some("english"))?;
///
/// match loaded {
/// LoadedPlugin::Standard(plugin) => {
/// println!("普通插件: {}", plugin.get_name());
/// }
/// LoadedPlugin::Localized(context) => {
/// println!("本地化插件: {}", context.plugin().get_name());
/// println!("STRING 文件数: {}", context.string_files().files.len());
/// }
/// }
/// ```
pub fn load_auto(
path: PathBuf,
language: Option<&str>,
) -> Result<Self, Box<dyn std::error::Error>> {
// ⚡ 性能优化:只加载一次 ESP 文件
let plugin = Plugin::load(path.clone())?;
// 检查是否为本地化插件
if plugin.is_localized() {
// 本地化插件:使用已加载的 Plugin 创建上下文(避免重复加载)
let lang = language.unwrap_or("english");
// ✅ 使用 new_with_plugin 复用已加载的 Plugin
match LocalizedPluginContext::new_with_plugin(plugin, path.clone(), lang) {
Ok(context) => Ok(LoadedPlugin::Localized(context)),
Err(e) => {
// STRING 文件加载失败,需要重新加载 Plugin
// 因为 plugin 的所有权已经转移到 new_with_plugin 中
eprintln!(
"警告: STRING 文件加载失败: {}",
e
);
eprintln!("降级为普通插件模式(字符串将显示为 StringID)");
// 重新加载 Plugin(仅在 STRING 加载失败时)
let fallback_plugin = Plugin::load(path)?;
Ok(LoadedPlugin::Standard(fallback_plugin))
}
}
} else {
// 普通插件:直接返回
Ok(LoadedPlugin::Standard(plugin))
}
}
/// 获取底层 Plugin 的引用(无论哪种类型)
pub fn plugin(&self) -> &Plugin {
match self {
LoadedPlugin::Standard(plugin) => plugin,
LoadedPlugin::Localized(context) => context.plugin(),
}
}
/// 转移所有权获取底层 Plugin(无论哪种类型)
///
/// 如果是本地化插件,将丢弃 StringFileSet。
/// 如果需要保留 STRING 文件,使用 `match` 解构 `LoadedPlugin` 获取完整的 `LocalizedPluginContext`。
pub fn into_plugin(self) -> Plugin {
match self {
LoadedPlugin::Standard(plugin) => plugin,
LoadedPlugin::Localized(context) => {
let (plugin, _string_files, _language) = context.into_parts();
plugin
}
}
}
/// 检查是否为本地化插件
pub fn is_localized(&self) -> bool {
matches!(self, LoadedPlugin::Localized(_))
}
}
// 便捷方法:提取字符串(统一接口)
impl LoadedPlugin {
/// 提取字符串(自动处理本地化/非本地化)
pub fn extract_strings(&self) -> Vec<crate::ExtractedString> {
self.plugin().extract_strings()
}
/// 获取插件名称
pub fn get_name(&self) -> &str {
self.plugin().get_name()
}
/// 获取统计信息
pub fn get_stats(&self) -> crate::plugin::PluginStats {
self.plugin().get_stats()
}
}
#[cfg(test)]
mod tests {
// use super::*;
#[test]
fn test_load_auto_concept() {
// 测试概念验证
// 实际测试需要真实的 ESP 文件
// let loaded = LoadedPlugin::load_auto("test.esp".into(), None).unwrap();
// match loaded {
// LoadedPlugin::Standard(plugin) => {
// assert!(!plugin.is_localized());
// }
// LoadedPlugin::Localized(context) => {
// assert!(context.plugin().is_localized());
// }
// }
}
}