use crate::userscript_api::{
fs_api::path_obj::PathObj,
include::{
Lua, LuaExternalError, LuaFunction, LuaResult, LuaTable, LuaTableSequence, LuaUserData,
LuaUserDataRef,
},
};
use serde::Serialize;
#[derive(Serialize, Debug)]
pub struct ScanResult {
pub engine: String,
pub item: DataItemResult,
}
impl LuaUserData for ScanResult {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("engine", |_, this: &ScanResult| Ok(this.engine.clone()));
fields.add_field_method_get("item", |_, this: &ScanResult| Ok(this.item.clone()));
}
}
#[derive(Serialize, Debug, Clone)]
pub struct DataItemResult {
pub name: String,
pub path: Option<PathObj>,
}
impl LuaUserData for DataItemResult {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("name", |_, this: &DataItemResult| Ok(this.name.clone()));
fields.add_field_method_get("path", |_, this: &DataItemResult| Ok(this.path.clone()));
}
}
pub(super) async fn add_csv_method(lua: &Lua, results: &LuaTable) -> LuaResult<()> {
let csv_method: LuaFunction =
lua.create_async_function(|_, (this, headers): (LuaTable, Option<bool>)| async move {
let mut scan_results: LuaTableSequence<'_, LuaUserDataRef<ScanResult>> =
this.sequence_values::<LuaUserDataRef<ScanResult>>();
let mut rows: Vec<String> = Vec::with_capacity(
usize::try_from(this.len()?).map_err(LuaExternalError::into_lua_err)? + 1,
);
if headers.is_some_and(|headers: bool| headers) {
let headers: String = r#""Scan Engine","Item Name","Item Path""#.to_string();
rows.push(headers);
}
while let Some(Ok(scan_result)) = scan_results.next() {
let row: String = format!(
r#""{}","{}","{}""#,
scan_result.engine,
scan_result.item.name,
scan_result
.item
.path
.clone()
.unwrap_or_default()
.0
.to_string_lossy()
);
rows.push(row);
}
let mut csv: String = rows.join("\n");
csv.push('\n');
Ok(csv)
})?;
results.set("csv", csv_method)?;
Ok(())
}
pub(super) async fn add_json_method(lua: &Lua, results: &LuaTable) -> LuaResult<()> {
let json_method: LuaFunction =
lua.create_async_function(|_, (this, pretty): (LuaTable, Option<bool>)| async move {
let mut scan_results: LuaTableSequence<'_, LuaUserDataRef<ScanResult>> =
this.sequence_values::<LuaUserDataRef<ScanResult>>();
let mut rows: Vec<ScanResult> = Vec::with_capacity(
usize::try_from(this.len()?).map_err(LuaExternalError::into_lua_err)?,
);
while let Some(Ok(scan_result)) = scan_results.next() {
let result: ScanResult = ScanResult {
engine: scan_result.engine.clone(),
item: scan_result.item.clone(),
};
rows.push(result);
}
let serialized: String = if pretty.is_some_and(|pretty: bool| pretty) {
serde_json::to_string_pretty(&rows)
} else {
serde_json::to_string(&rows)
}
.map_err(LuaExternalError::into_lua_err)?;
Ok(serialized)
})?;
results.set("json", json_method)?;
Ok(())
}
pub(super) async fn add_ndjson_method(lua: &Lua, results: &LuaTable) -> LuaResult<()> {
let json_method: LuaFunction = lua.create_async_function(|_, this: LuaTable| async move {
let mut scan_results: LuaTableSequence<'_, LuaUserDataRef<ScanResult>> =
this.sequence_values::<LuaUserDataRef<ScanResult>>();
let mut rows: Vec<ScanResult> = Vec::with_capacity(
usize::try_from(this.len()?).map_err(LuaExternalError::into_lua_err)?,
);
while let Some(Ok(scan_result)) = scan_results.next() {
let result: ScanResult = ScanResult {
engine: scan_result.engine.clone(),
item: scan_result.item.clone(),
};
rows.push(result);
}
let mut ndjson: Vec<String> = Vec::with_capacity(rows.len());
for row in rows {
let serialized: String =
serde_json::to_string(&row).map_err(LuaExternalError::into_lua_err)?;
ndjson.push(serialized);
}
let serialized: String = ndjson.join("\n");
Ok(serialized)
})?;
results.set("ndjson", json_method)?;
Ok(())
}