Closed1

RustのCLIでluaのコンフィグファイルを使いたいときのサンプル

shuntakashuntaka
use mlua::prelude::*;
use mlua::{Lua, Result, Table, Value};
use std::fs;

#[derive(Debug)]
pub struct Config {
    pub config_path: String,
    pub slack_token: String,
}

pub fn init() -> anyhow::Result<Config> {
    let config_path = get_config_path();

    Ok(load_config(&config_path)?)
}

fn get_config_path() -> String {
    if let Ok(path) = std::env::var("CLI_NAME_CONFIG_PATH") {
        if !path.trim().is_empty() {
            return path;
        }
    }

    let home_dir = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
    format!("{}/.config/cliname", home_dir)
}

fn load_config(config_path: &str) -> Result<Config> {
    let lua = Lua::new();
    lua.load(format!(
        r#"package.path = package.path .. ";{}/?.lua""#,
        config_path
    ))
    .exec()?;

    let cliname_mod = lua.create_table()?;
    let config_sub_mod = lua.create_table()?;
    config_sub_mod.set("config_path", config_path)?;
    cliname_mod.set("config", config_sub_mod)?;

    let globals = lua.globals();
    let package: LuaTable = globals.get("package")?;
    let loaded: LuaTable = package.get("loaded")?;
    loaded.set("cliname", cliname_mod)?;

    let config_code = fs::read_to_string(&format!("{}/config.lua", config_path))?;
    let config_eval = lua.load(&config_code).eval()?;

    if let Value::Table(config_tbl) = config_eval {
        let config_path: String = match config_tbl.get::<_, Option<String>>("config_path")? {
            Some(v) => v,
            None => config_path.to_string(),
        };
        let slack_tbl: Table = config_tbl.get("slack")?;
        let token: String = slack_tbl.get("token")?;

        Ok(Config {
            config_path,
            slack_token: token,
        })
    } else {
        Err(mlua::Error::RuntimeError(
            "config.lua did not return a table!".to_string(),
        ))
    }
}

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

    #[test]
    fn test_load_config() -> Result<()> {
        let temp_dir = tempfile::tempdir()?;
        let config_path = temp_dir.path();

        let config_file_path = config_path.join("config.lua");
        let secrets_file_path = config_path.join("secrets.lua");

        let config_code = r#"
local secrets = require("secrets")
local cliname = require("cliname")

return {
  slack = {
    token = secrets.slack.token,
  },
}
"#;
        fs::write(&config_file_path, config_code)?;

        let secrets_code = r#"
local M = {}
M.slack = {
    token = "test_token"
}
return M
"#;
        fs::write(&secrets_file_path, secrets_code)?;

        let config = load_config(&config_path.to_string_lossy())?;

        assert_eq!(config.config_path, config_path.to_string_lossy());
        assert_eq!(config.slack_token, "test_token");

        Ok(())
    }
}

このスクラップは5ヶ月前にクローズされました