emmylua_code_analysis/resources/
mod.rs1mod best_resource_path;
2
3use std::path::{Path, PathBuf};
4
5pub use best_resource_path::get_best_resources_dir;
6use include_dir::{Dir, DirEntry, include_dir};
7
8use crate::{LuaFileInfo, get_locale_code, load_workspace_files};
9
10static RESOURCE_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/resources");
11const VERSION: &str = env!("CARGO_PKG_VERSION");
12
13pub fn load_resource_std(
14 create_resources_dir: Option<String>,
15 is_jit: bool,
16) -> (PathBuf, Vec<LuaFileInfo>) {
17 if let Some(create_resources_dir) = create_resources_dir {
19 let resource_path = if create_resources_dir.is_empty() {
20 get_best_resources_dir()
21 } else {
22 PathBuf::from(&create_resources_dir)
23 };
24 let std_dir = get_std_dir(&resource_path);
26 let result = load_resource_from_file_system(&resource_path);
27 if let Some(mut files) = result {
28 if !is_jit {
29 remove_jit_resource(&mut files);
30 }
31 return (std_dir, files);
32 }
33 }
34 let resoucres_dir = get_best_resources_dir();
36 let std_dir = resoucres_dir.join("std");
37 let files = load_resource_from_include_dir();
38 let mut files = files
39 .into_iter()
40 .filter_map(|file| {
41 if file.path.ends_with(".lua") {
42 let path = resoucres_dir
43 .join(&file.path)
44 .to_str()
45 .expect("UTF-8 paths")
46 .to_string();
47 Some(LuaFileInfo {
48 path,
49 content: file.content,
50 })
51 } else {
52 None
53 }
54 })
55 .collect::<_>();
56 if !is_jit {
57 remove_jit_resource(&mut files);
58 }
59 (std_dir, files)
60}
61
62fn remove_jit_resource(files: &mut Vec<LuaFileInfo>) {
63 const JIT_FILES_TO_REMOVE: &[&str] = &[
64 "jit.lua",
65 "jit/profile.lua",
66 "jit/util.lua",
67 "string/buffer.lua",
68 "table/clear.lua",
69 "table/new.lua",
70 "ffi.lua",
71 ];
72 files.retain(|file| {
73 let path = Path::new(&file.path);
74 !JIT_FILES_TO_REMOVE
75 .iter()
76 .any(|suffix| path.ends_with(suffix))
77 });
78}
79
80fn load_resource_from_file_system(resources_dir: &Path) -> Option<Vec<LuaFileInfo>> {
81 if check_need_dump_to_file_system() {
83 log::info!("Creating resources dir: {:?}", resources_dir);
84 let files = load_resource_from_include_dir();
85 for file in &files {
86 let path = resources_dir.join(&file.path);
87 let parent = path.parent()?;
88 if !parent.exists() {
89 match std::fs::create_dir_all(parent) {
90 Ok(_) => {}
91 Err(e) => {
92 log::error!("Failed to create dir: {:?}, {:?}", parent, e);
93 return None;
94 }
95 }
96 }
97
98 match std::fs::write(&path, &file.content) {
99 Ok(_) => {}
100 Err(e) => {
101 log::error!("Failed to write file: {:?}, {:?}", path, e);
102 return None;
103 }
104 }
105 }
106
107 let version_path = resources_dir.join("version");
108 let content = VERSION.to_string();
109 match std::fs::write(&version_path, content) {
110 Ok(_) => {}
111 Err(e) => {
112 log::error!("Failed to write file: {:?}, {:?}", version_path, e);
113 return None;
114 }
115 }
116 }
117
118 let std_dir = get_std_dir(&resources_dir);
119 let match_pattern = vec!["**/*.lua".to_string()];
120 let files = match load_workspace_files(&std_dir, &match_pattern, &Vec::new(), &Vec::new(), None)
121 {
122 Ok(files) => files,
123 Err(e) => {
124 log::error!("Failed to load std lib: {:?}", e);
125 vec![]
126 }
127 };
128
129 Some(files)
130}
131
132fn check_need_dump_to_file_system() -> bool {
133 if cfg!(debug_assertions) {
134 return true;
135 }
136
137 let resoucres_dir = get_best_resources_dir();
138 let version_path = resoucres_dir.join("version");
139
140 if !version_path.exists() {
141 return true;
142 }
143
144 let Ok(content) = std::fs::read_to_string(&version_path) else {
145 return true;
146 };
147 let version = content.trim();
148 if version != VERSION {
149 return true;
150 }
151
152 false
153}
154
155pub fn load_resource_from_include_dir() -> Vec<LuaFileInfo> {
156 let mut files = Vec::new();
157 walk_resource_dir(&RESOURCE_DIR, &mut files);
158 files
159}
160
161fn walk_resource_dir(dir: &Dir, files: &mut Vec<LuaFileInfo>) {
162 for entry in dir.entries() {
163 match entry {
164 DirEntry::File(file) => {
165 let path = file.path();
166 let content = file.contents_utf8().expect("UTF-8 paths");
167
168 files.push(LuaFileInfo {
169 path: path.to_str().expect("UTF-8 paths").to_string(),
170 content: content.to_string(),
171 });
172 }
173 DirEntry::Dir(subdir) => {
174 walk_resource_dir(subdir, files);
175 }
176 }
177 }
178}
179
180fn get_std_dir(resources_dir: &Path) -> PathBuf {
182 let locale = get_locale_code(&rust_i18n::locale());
183 if locale != "en" {
184 let locale_dir = resources_dir.join(format!("std-{locale}"));
185 if locale_dir.exists() {
186 return locale_dir;
187 }
188 }
189 resources_dir.join("std")
190}