smart-patcher 0.7.0

Patcher based on rules
Documentation
use anyhow::{anyhow, bail};
use rhai::{Dynamic, Engine, Scope};
use std::env;
use std::path::Path;

pub fn find_by(
  patch_dir: &Path,
  path: &Path,
  content: &str,
  start_pos: Option<usize>,
  end_pos: Option<usize>,
) -> anyhow::Result<(usize, usize, bool)> {
  let patch_path = patch_dir.to_path_buf().join(path);

  if !patch_path.is_file() {
    bail!("Rhai script path ({:?}) has to be a file.", patch_path)
  }
  let engine = Engine::new();

  let rev = env::current_dir()?;
  env::set_current_dir(patch_dir)?;

  let script_content = std::fs::read_to_string(patch_path)?;
  let ast = engine.compile(&script_content)?;
  let result: Dynamic = engine
    .call_fn(
      &mut Scope::new(),
      &ast,
      "find",
      (
        content.to_owned(),
        start_pos.unwrap_or(0),
        end_pos.unwrap_or(content.len()),
      ),
    )
    .map_err(|e| anyhow!("Call function error: {}", e))?;

  let result_array = result
    .into_array()
    .map_err(|e| anyhow!("Convert into array error: {}", e))?;
  if result_array.len() != 3 {
    bail!("Expected 3 values from find function");
  }

  let start = result_array[0]
    .clone()
    .try_cast::<usize>()
    .ok_or(anyhow!("Cast into `Option<usize>` error"))?;
  let end = result_array[1]
    .clone()
    .try_cast::<usize>()
    .ok_or(anyhow!("Cast into `Option<usize>` error"))?;
  let found = result_array[2]
    .clone()
    .try_cast::<bool>()
    .ok_or(anyhow!("Cast into `Option<usize>` error"))?;

  env::set_current_dir(rev)?;

  Ok((start, end, found))
}

pub fn replace_by(patch_dir: &Path, path: &Path, content: &str) -> anyhow::Result<String> {
  let patch_path = patch_dir.to_path_buf().join(path);

  if !patch_path.is_file() {
    bail!("Rhai script path ({:?}) has to be a file.", patch_path)
  }
  let engine = Engine::new();

  let rev = env::current_dir()?;
  env::set_current_dir(patch_dir)?;

  let script_content = std::fs::read_to_string(patch_path)?;
  let ast = engine.compile(&script_content)?;
  let result: Dynamic = engine
    .call_fn(&mut Scope::new(), &ast, "replace", (content.to_owned(),))
    .map_err(|e| anyhow!("Call function error: {}", e))?;

  let result_str = result
    .into_string()
    .map_err(|e| anyhow!("Convert into string error: {}", e))?;
  env::set_current_dir(rev)?;

  Ok(result_str)
}

pub fn decode_by(patch_dir: &Path, path: &Path, content: &[u8]) -> anyhow::Result<String> {
  let patch_path = patch_dir.to_path_buf().join(path);

  if !patch_path.is_file() {
    bail!("Rhai script path ({:?}) has to be a file.", patch_path)
  }
  let engine = Engine::new();

  let rev = env::current_dir()?;
  env::set_current_dir(patch_dir)?;

  let script_content = std::fs::read_to_string(patch_path)?;
  let ast = engine.compile(&script_content)?;
  let result: Dynamic = engine
    .call_fn(&mut Scope::new(), &ast, "decode", (content.to_owned(),))
    .map_err(|e| anyhow!("Call function error: {}", e))?;

  let result_str = result
    .into_string()
    .map_err(|e| anyhow!("Convert into string error: {}", e))?;
  env::set_current_dir(rev)?;

  Ok(result_str)
}

pub fn encode_by(patch_dir: &Path, path: &Path, content: &str) -> anyhow::Result<Vec<u8>> {
  let patch_path = patch_dir.to_path_buf().join(path);

  if !patch_path.is_file() {
    bail!("Rhai script path ({:?}) has to be a file.", patch_path)
  }
  let engine = Engine::new();

  let rev = env::current_dir()?;
  env::set_current_dir(patch_dir)?;

  let script_content = std::fs::read_to_string(patch_path)?;
  let ast = engine.compile(&script_content)?;
  let result: Dynamic = engine
    .call_fn(&mut Scope::new(), &ast, "encode", (content.to_owned(),))
    .map_err(|e| anyhow!("Call function error: {}", e))?;

  let result_vec = result
    .into_typed_array::<u8>()
    .map_err(|e| anyhow!("Convert into `Vec<u8>` error: {}", e))?;
  env::set_current_dir(rev)?;

  Ok(result_vec)
}