s3sync 1.58.6

Reliable, flexible, and fast synchronization tool for S3.
Documentation
use crate::lua::lua_script_callback_engine::LuaScriptCallbackEngine;
use crate::types::event_callback::{EventCallback, EventData};
use anyhow::Result;
use async_trait::async_trait;
use tracing::warn;

pub struct LuaEventCallback {
    lua: LuaScriptCallbackEngine,
}

impl LuaEventCallback {
    #[allow(clippy::new_without_default)]
    pub fn new(
        memory_limit: usize,
        allow_lua_os_library: bool,
        unsafe_lua: bool,
        callback_timeout_milliseconds: u64,
    ) -> Self {
        let lua = if unsafe_lua {
            LuaScriptCallbackEngine::unsafe_new(memory_limit, callback_timeout_milliseconds)
        } else if allow_lua_os_library {
            LuaScriptCallbackEngine::new(memory_limit, callback_timeout_milliseconds)
        } else {
            LuaScriptCallbackEngine::new_without_os_io_libs(
                memory_limit,
                callback_timeout_milliseconds,
            )
        };

        Self { lua }
    }

    pub fn load_and_compile(&mut self, script_path: &str) -> Result<()> {
        let lua_script = std::fs::read(script_path)?;
        self.lua.load_and_compile(&String::from_utf8(lua_script)?)
    }
}

#[async_trait]
impl EventCallback for LuaEventCallback {
    async fn on_event(&mut self, event_data: EventData) {
        let event_data_lua = self.lua.get_engine().create_table().unwrap();
        event_data_lua
            .set("event_type", event_data.event_type.bits())
            .unwrap();
        event_data_lua.set("dry_run", event_data.dry_run).unwrap();
        event_data_lua.set("key", event_data.key.clone()).unwrap();
        event_data_lua
            .set("source_version_id", event_data.source_version_id.clone())
            .unwrap();
        event_data_lua
            .set("target_version_id", event_data.target_version_id.clone())
            .unwrap();
        event_data_lua
            .set(
                "source_last_modified",
                event_data.source_last_modified.map(|dt| dt.to_string()),
            )
            .unwrap();
        event_data_lua
            .set(
                "target_last_modified",
                event_data.target_last_modified.map(|dt| dt.to_string()),
            )
            .unwrap();
        event_data_lua
            .set("source_size", event_data.source_size)
            .unwrap();
        event_data_lua
            .set("target_size", event_data.target_size)
            .unwrap();
        event_data_lua
            .set(
                "checksum_algorithm",
                event_data.checksum_algorithm.map(|alg| alg.to_string()),
            )
            .unwrap();
        event_data_lua
            .set("source_checksum", event_data.source_checksum.clone())
            .unwrap();
        event_data_lua
            .set("target_checksum", event_data.target_checksum.clone())
            .unwrap();
        event_data_lua
            .set("source_etag", event_data.source_etag.clone())
            .unwrap();
        event_data_lua
            .set("target_etag", event_data.target_etag.clone())
            .unwrap();
        event_data_lua
            .set(
                "source_content_type",
                event_data.source_content_type.clone(),
            )
            .unwrap();
        event_data_lua
            .set(
                "source_user_defined_metadata",
                event_data.source_user_defined_metadata.clone(),
            )
            .unwrap();
        event_data_lua
            .set("source_tagging", event_data.source_tagging.clone())
            .unwrap();
        event_data_lua
            .set("byte_written", event_data.byte_written)
            .unwrap();
        event_data_lua
            .set("upload_id", event_data.upload_id.clone())
            .unwrap();
        event_data_lua
            .set("part_number", event_data.part_number)
            .unwrap();
        event_data_lua
            .set("message", event_data.message.clone())
            .unwrap();
        event_data_lua
            .set("stats_transferred_byte", event_data.stats_transferred_byte)
            .unwrap();
        event_data_lua
            .set(
                "stats_transferred_byte_per_sec",
                event_data.stats_transferred_byte_per_sec,
            )
            .unwrap();
        event_data_lua
            .set(
                "stats_transferred_object",
                event_data.stats_transferred_object,
            )
            .unwrap();
        event_data_lua
            .set(
                "stats_transferred_object_per_sec",
                event_data.stats_transferred_object_per_sec,
            )
            .unwrap();
        event_data_lua
            .set("stats_etag_verified", event_data.stats_etag_verified)
            .unwrap();
        event_data_lua
            .set("stats_etag_mismatch", event_data.stats_etag_mismatch)
            .unwrap();
        event_data_lua
            .set(
                "stats_checksum_verified",
                event_data.stats_checksum_verified,
            )
            .unwrap();
        event_data_lua
            .set(
                "stats_checksum_mismatch",
                event_data.stats_checksum_mismatch,
            )
            .unwrap();
        event_data_lua
            .set("stats_deleted", event_data.stats_deleted)
            .unwrap();
        event_data_lua
            .set("stats_skipped", event_data.stats_skipped)
            .unwrap();
        event_data_lua
            .set("stats_error", event_data.stats_error)
            .unwrap();
        event_data_lua
            .set("stats_warning", event_data.stats_warning)
            .unwrap();
        event_data_lua
            .set("stats_duration_sec", event_data.stats_duration_sec)
            .unwrap();

        self.lua.reset_deadline();

        let func: mlua::Result<mlua::Function> = self.lua.get_engine().globals().get("on_event");
        if let Err(e) = func {
            warn!("Lua function 'on_event' not found: {}", e);
            return;
        }

        let func_result: mlua::Result<()> = func.unwrap().call_async(event_data_lua).await;
        if let Err(e) = func_result {
            warn!("Error executing Lua event callback: {}", e);
            return;
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn create_callback() {
        let _callback = LuaEventCallback::new(8 * 1024 * 1024, false, false, 0);
        let _callback = LuaEventCallback::new(8 * 1024 * 1024, true, false, 0);
        let _callback = LuaEventCallback::new(0, true, true, 0);
    }
}