luaur_analyze_cli/methods/
cli_config_resolver_read_config_rec.rs1use crate::records::cli_config_resolver::CliConfigResolver;
2use crate::records::luau_config_interrupt_info::LuauConfigInterruptInfo;
3use alloc::format;
4use alloc::rc::Rc;
5use alloc::string::{String, ToString};
6use core::ffi::c_int;
7use core::ffi::c_void;
8use luaur_analysis::records::time_limit_error::TimeLimitError;
9use luaur_analysis::records::type_check_limits::TypeCheckLimits;
10use luaur_analysis::records::user_cancel_error::UserCancelError;
11use luaur_cli_lib::functions::get_parent_path::get_parent_path;
12use luaur_cli_lib::functions::is_file::is_file;
13use luaur_cli_lib::functions::join_paths_file_utils_alt_b::join_paths_string_view_string_view;
14use luaur_cli_lib::functions::read_file::read_file;
15use luaur_common::functions::get_clock::get_clock;
16use luaur_config::functions::extract_luau_config::extract_luau_config;
17use luaur_config::functions::parse_config::parse_config;
18use luaur_config::records::alias_options::AliasOptions;
19use luaur_config::records::config::Config;
20use luaur_config::records::config_options::ConfigOptions;
21use luaur_config::records::interrupt_callbacks::InterruptCallbacks;
22use luaur_vm::functions::lua_getthreaddata::lua_getthreaddata;
23use luaur_vm::functions::lua_setthreaddata::lua_setthreaddata;
24use luaur_vm::type_aliases::lua_state::lua_State;
25
26const K_CONFIG_NAME: &str = ".luaurc";
28const K_LUAU_CONFIG_NAME: &str = ".config.luau";
29
30pub(crate) unsafe extern "C-unwind" fn luau_config_interrupt(l: *mut lua_State, _gc: c_int) {
45 let info = lua_getthreaddata(l) as *const LuauConfigInterruptInfo;
46 if info.is_null() {
47 return;
48 }
49 let info = &*info;
50
51 if let Some(finish_time) = info.limits.finishTime() {
52 if get_clock() > finish_time {
53 std::panic::panic_any(TimeLimitError::time_limit_error_time_limit_error(
54 &info.module,
55 ));
56 }
57 }
58 if let Some(token) = info.limits.cancellationToken() {
59 if token.requested() {
60 std::panic::panic_any(UserCancelError::new(info.module.clone()));
61 }
62 }
63}
64
65impl CliConfigResolver {
66 pub fn read_config_rec(&mut self, path: &str, limits: &TypeCheckLimits) -> &Config {
69 if self.config_cache.contains_key(path) {
71 return &self.config_cache[path];
72 }
73
74 let mut result: Config = match get_parent_path(path) {
77 Some(parent) => self.read_config_rec(&parent, limits).clone(),
78 None => self.default_config.clone(),
79 };
80
81 let config_path_candidate = join_paths_string_view_string_view(path, K_CONFIG_NAME);
84 let config_path: Option<String> = if is_file(&config_path_candidate) {
85 Some(config_path_candidate)
86 } else {
87 None
88 };
89
90 let luau_config_path_candidate =
93 join_paths_string_view_string_view(path, K_LUAU_CONFIG_NAME);
94 let luau_config_path: Option<String> = if is_file(&luau_config_path_candidate) {
95 Some(luau_config_path_candidate)
96 } else {
97 None
98 };
99
100 if config_path.is_some() && luau_config_path.is_some() {
101 let ambiguous_error = format!(
103 "Both {} and {} files exist",
104 K_CONFIG_NAME, K_LUAU_CONFIG_NAME
105 );
106 self.config_errors
107 .push((config_path.clone().unwrap(), ambiguous_error));
108 } else if let Some(config_path) = config_path.as_ref() {
109 if let Some(contents) = read_file(config_path) {
111 let alias_opts = AliasOptions {
112 config_location: Some(config_path.clone()),
113 overwrite_aliases: true,
114 };
115
116 let opts = ConfigOptions {
117 compat: false,
118 alias_options: Some(alias_opts),
119 };
120
121 if let Some(error) = parse_config(&contents, &mut result, &opts) {
123 self.config_errors.push((config_path.clone(), error));
124 }
125 }
126 } else if let Some(luau_config_path) = luau_config_path.as_ref() {
127 if let Some(contents) = read_file(luau_config_path) {
129 let alias_opts = AliasOptions {
133 config_location: config_path.clone(),
134 overwrite_aliases: true,
135 };
136
137 let mut info = LuauConfigInterruptInfo {
141 limits: limits.clone(),
142 module: luau_config_path.clone(),
143 };
144 let info_ptr: *mut LuauConfigInterruptInfo = &mut info;
145
146 let callbacks = InterruptCallbacks {
147 init_callback: Some(Rc::new(move |l: *mut lua_State| unsafe {
148 lua_setthreaddata(l, info_ptr as *mut c_void);
149 })),
150 interrupt_callback: Some(luau_config_interrupt),
151 };
152
153 if let Some(error) =
155 extract_luau_config(&contents, &mut result, Some(alias_opts), callbacks)
156 {
157 self.config_errors.push((luau_config_path.clone(), error));
158 }
159 }
160 }
161
162 self.config_cache.insert(path.to_string(), result);
164 &self.config_cache[path]
165 }
166}