lulu 0.0.721

A mini lua runtime
Documentation
use base64::prelude::*;

#[derive(Clone)]
pub struct LuluByteArray {
  pub bytes: Vec<u8>,
}

impl mlua::UserData for LuluByteArray {
  fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
    methods.add_method("to_table", |_, this, ()| Ok(this.bytes.clone()));
    methods.add_method("len", |_, this, ()| Ok(this.bytes.len()));
    methods.add_method("to_hex", |_, this, ()| {
      Ok(
        this
          .bytes
          .iter()
          .map(|b| format!("{:02x}", b))
          .collect::<String>(),
      )
    });

    methods.add_method("to_string", |_lua, this, encoding: Option<String>| {
      let enc_name = encoding.unwrap_or_else(|| "utf-8".to_string());
      match enc_name.to_lowercase().as_str() {
        "utf-8" => Ok(String::from_utf8_lossy(&this.bytes).to_string()),
        _ => Err(mlua::Error::RuntimeError(format!(
          "Unsupported encoding '{}'",
          enc_name
        ))),
      }
    });

    methods.add_method("to_str", |_lua, this, ()| _lua.create_string(&this.bytes));

    methods.add_method_mut("extend", |_, this, other: mlua::AnyUserData| {
      let other_bytes = other.borrow::<LuluByteArray>()?;
      this.bytes.extend(&other_bytes.bytes);
      Ok(())
    });

    methods.add_method_mut("extend_table", |_, this, other: Vec<u8>| {
      this.bytes.extend(other);
      Ok(())
    });

    methods.add_method("to_base64", |_, this, ()| {
      Ok(BASE64_STANDARD.encode(&this.bytes))
    });

    methods.add_method_mut("push", |_, this, byte: u8| {
      this.bytes.push(byte);
      Ok(())
    });

    methods.add_method_mut("pop", |_, this, ()| Ok(this.bytes.pop()));

    methods.add_method_mut("clear", |_, this, ()| {
      this.bytes.clear();
      Ok(())
    });

    methods.add_method("slice", |_, this, (start, stop): (usize, usize)| {
      let start = start.saturating_sub(1);
      let stop = stop.min(this.bytes.len());
      Ok(LuluByteArray {
        bytes: this.bytes[start..stop].to_vec(),
      })
    });

    methods.add_method("copy", |_, this, ()| {
      Ok(LuluByteArray {
        bytes: this.bytes.clone(),
      })
    });

    methods.add_method("new", |_, _, bytes: Vec<u8>| Ok(LuluByteArray { bytes }));

    methods.add_method("map", |_, this, func: mlua::Function| {
      let mapped = this
        .bytes
        .iter()
        .map(|b| func.call::<u8>(*b).unwrap_or(*b))
        .collect();
      Ok(LuluByteArray { bytes: mapped })
    });
  }
}

impl mlua::FromLua for LuluByteArray {
  fn from_lua(value: mlua::Value, _lua: &mlua::Lua) -> mlua::Result<Self> {
    match value {
      mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
      mlua::Value::String(s) => Ok(LuluByteArray {
        bytes: s.as_bytes().to_vec(),
      }),
      _ => Err(mlua::Error::FromLuaConversionError {
        from: value.type_name(),
        to: "ByteArray".to_string(),
        message: None,
      }),
    }
  }
}