use crate::runtime::Runtime;
use crate::script::LuaValueExt;
use crate::script::aip_modules::support::{base_dir_and_globs, list_files_with_options};
use crate::support::{AsStrsExt, W};
use crate::types::Extrude;
use crate::{Error, Result};
use mlua::{IntoLua, Lua, MultiValue, Table, Value};
use udiffx::ApplyChangesStatus;
pub fn init_module(lua: &Lua, runtime: &Runtime) -> Result<Table> {
let table = lua.create_table()?;
let runtime_inner = runtime.clone();
let apply_file_changes_fn = lua.create_function(move |lua, args| apply_file_changes(lua, &runtime_inner, args))?;
let runtime_inner = runtime.clone();
let load_files_context_fn = lua.create_function(move |lua, args| load_files_context(lua, &runtime_inner, args))?;
let file_changes_instruction_fn = lua.create_function(file_changes_instruction)?;
table.set("apply_file_changes", apply_file_changes_fn)?;
table.set("load_files_context", load_files_context_fn)?;
table.set("file_changes_instruction", file_changes_instruction_fn)?;
Ok(table)
}
fn apply_file_changes(
lua: &Lua,
runtime: &Runtime,
(content, base_dir, options): (String, Option<String>, Option<Value>),
) -> mlua::Result<MultiValue> {
let extrude = match options {
Some(Value::Table(table)) => Extrude::extract_from_table_value(&table)?,
_ => None,
};
let do_extrude = matches!(extrude, Some(Extrude::Content));
let (file_changes, extruded_content) =
udiffx::extract_file_changes(&content, do_extrude).map_err(crate::Error::from)?;
let base_dir_str = base_dir.unwrap_or_default();
let abs_base_dir = runtime.resolve_path_default(base_dir_str.into(), None)?;
let status = udiffx::apply_file_changes(&abs_base_dir, file_changes).map_err(crate::Error::from)?;
let mut values = MultiValue::new();
values.push_back(W(status).into_lua(lua)?);
if do_extrude {
let remain = extruded_content.unwrap_or_default();
values.push_back(Value::String(lua.create_string(&remain)?));
}
Ok(values)
}
fn load_files_context(
lua: &Lua,
runtime: &Runtime,
(include_globs, options): (Value, Option<Value>),
) -> mlua::Result<Value> {
let (base_path, include_globs) = base_dir_and_globs(runtime, include_globs, options.as_ref())?;
let absolute = options.x_get_bool("absolute").unwrap_or(false);
let file_refs = list_files_with_options(runtime, base_path.as_ref(), &include_globs.x_as_strs(), absolute, true)?;
if file_refs.is_empty() {
return Ok(Value::Nil);
}
let base_path = match base_path {
Some(bp) => bp,
None => runtime
.dir_context()
.wks_dir()
.ok_or_else(|| Error::custom("Workspace dir is missing"))?
.clone(),
};
let mut context = String::new();
for file_ref in file_refs {
let full_path = if absolute {
file_ref.spath.clone()
} else {
base_path.join(&file_ref.spath)
};
let content = std::fs::read_to_string(full_path).map_err(Error::from)?;
if !context.is_empty() {
context.push('\n');
}
context.push_str(&format!(r#"<FILE_CONTENT path="{}">"#, file_ref.spath));
context.push('\n');
context.push_str(&content);
if !content.ends_with('\n') {
context.push('\n');
}
context.push_str("</FILE_CONTENT>\n");
}
match context.is_empty() {
false => Ok(Value::String(lua.create_string(&context)?)),
true => Ok(Value::Nil),
}
}
fn file_changes_instruction(lua: &Lua, (): ()) -> mlua::Result<Value> {
Ok(Value::String(lua.create_string(udiffx::prompt_file_changes())?))
}
impl IntoLua for W<ApplyChangesStatus> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
let status = self.0;
let mut total_count = 0;
let mut success_count = 0;
let mut fail_count = 0;
let table = lua.create_table()?;
let items_table = lua.create_table()?;
for (i, item) in status.items.iter().enumerate() {
total_count += 1;
if item.success() {
success_count += 1;
} else {
fail_count += 1;
}
let info_table = lua.create_table()?;
info_table.set("file_path", item.file_path())?;
info_table.set("kind", item.kind())?;
info_table.set("success", item.success())?;
info_table.set("error_msg", item.error_msg())?;
if !item.error_hunks.is_empty() {
let error_hunks_table = lua.create_table()?;
for (j, hunk) in item.error_hunks.iter().enumerate() {
let hunk_table = lua.create_table()?;
hunk_table.set("hunk_body", hunk.hunk_body.as_str())?;
hunk_table.set("cause", hunk.cause.as_str())?;
error_hunks_table.set(j + 1, hunk_table)?;
}
info_table.set("error_hunks", error_hunks_table)?;
}
items_table.set(i + 1, info_table)?;
}
table.set("total_count", total_count)?;
table.set("success_count", success_count)?;
table.set("fail_count", fail_count)?;
table.set("items", items_table)?;
table.set("success", fail_count == 0)?;
Ok(Value::Table(table))
}
}