shiden 0.1.2

The Shiden Language
#![allow(dead_code)]
use std::fs;
use std::path::Path;

pub mod helpers {
    use std::collections::BTreeMap;

    pub fn get_fs_helpers() -> BTreeMap<&'static str, Vec<u8>> {
        let mut m = BTreeMap::new();

        m.insert(
            "fs_read",
            vec![
                0x55, 0x48, 0x89, 0xE5, 0x48, 0x81, 0xEC, 0x00, 0x01, 0x00, 0x00, 0x53, 0x41, 0x54,
                0x41, 0x55, 0x49, 0x89, 0xF4, 0x49, 0x89, 0xD5, 0x48, 0x8D, 0x9D, 0x00, 0xFF, 0xFF,
                0xFF, 0x31, 0xC9, 0x48, 0x85, 0xFF, 0x74, 0x18, 0x48, 0x8B, 0x7F, 0x08, 0x48, 0x85,
                0xFF, 0x74, 0x0F, 0x48, 0x8B, 0x07, 0x88, 0x04, 0x0B, 0x48, 0x8B, 0x7F, 0x08, 0x48,
                0xFF, 0xC1, 0xEB, 0xED, 0xC6, 0x04, 0x0B, 0x00, 0x48, 0x89, 0xDF, 0x31, 0xF6, 0xB8,
                0x02, 0x00, 0x00, 0x00, 0x0F, 0x05, 0x48, 0x89, 0xC3, 0x48, 0x83, 0xFB, 0x00, 0x7C,
                0x22, 0x48, 0x89, 0xDF, 0x4C, 0x89, 0xE6, 0x4C, 0x89, 0xEA, 0xB8, 0x00, 0x00, 0x00,
                0x00, 0x0F, 0x05, 0x49, 0x89, 0xC4, 0x48, 0x89, 0xDF, 0xB8, 0x03, 0x00, 0x00, 0x00,
                0x0F, 0x05, 0x4C, 0x89, 0xE0, 0xEB, 0x07, 0x48, 0xC7, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
                0x41, 0x5D, 0x41, 0x5C, 0x5B, 0x48, 0x89, 0xEC, 0x5D, 0xC3,
            ],
        );

        m
    }
}

fn cstr_to_string(ptr: usize, memory: &[u8]) -> Result<String, String> {
    if ptr == 0 {
        return Err("Null pointer".to_string());
    }

    let mut result = String::new();
    let mut i = ptr;
    while i < memory.len() && memory[i] != 0 {
        result.push(memory[i] as char);
        i += 1;
    }
    Ok(result)
}

pub fn allocate_string(content: &str) -> (usize, Vec<u8>) {
    let bytes = content.as_bytes().to_vec();
    let len = bytes.len();
    (len, bytes)
}

pub fn fs_read(path: &str) -> Result<String, String> {
    fs::read_to_string(path).map_err(|e| format!("Failed to read file '{}': {}", path, e))
}

pub fn fs_write(path: &str, content: &str) -> Result<(), String> {
    fs::write(path, content).map_err(|e| format!("Failed to write file '{}': {}", path, e))
}

pub fn fs_exists(path: &str) -> i64 {
    if Path::new(path).exists() { 1 } else { 0 }
}

pub fn fs_delete_file(path: &str) -> Result<(), String> {
    fs::remove_file(path).map_err(|e| format!("Failed to delete file '{}': {}", path, e))
}

pub fn fs_file_size(path: &str) -> Result<i64, String> {
    fs::metadata(path)
        .map(|m| m.len() as i64)
        .map_err(|e| format!("Failed to get file size '{}': {}", path, e))
}

pub fn fs_mkdir(path: &str) -> Result<(), String> {
    fs::create_dir_all(path).map_err(|e| format!("Failed to create directory '{}': {}", path, e))
}

pub fn fs_list_dir(path: &str) -> Result<String, String> {
    let entries =
        fs::read_dir(path).map_err(|e| format!("Failed to list directory '{}': {}", path, e))?;

    let mut files = Vec::new();
    for entry in entries {
        match entry {
            Ok(e) => {
                if let Ok(name) = e.file_name().into_string() {
                    files.push(name);
                }
            }
            Err(_) => continue,
        }
    }

    Ok(files.join("\n"))
}

pub fn fs_edit(path: &str, key: &str, value: &str) -> Result<(), String> {
    fs_edit_strategy(path, key, value, "replace")
}

pub fn fs_edit_strategy(path: &str, key: &str, value: &str, strategy: &str) -> Result<(), String> {
    let content = fs::read_to_string(path).unwrap_or_else(|_| "{}".to_string());

    let mut json: serde_json::Value =
        serde_json::from_str(&content).map_err(|e| format!("Invalid JSON in '{}': {}", path, e))?;

    let parsed_value: serde_json::Value = serde_json::from_str(value)
        .unwrap_or_else(|_| serde_json::Value::String(value.to_string()));

    match strategy {
        "replace" => {
            json[key] = parsed_value;
        }
        "merge" => {
            if let (Some(obj), serde_json::Value::Object(val_map)) =
                (json.as_object_mut(), &parsed_value)
            {
                for (k, v) in val_map {
                    obj.insert(k.clone(), v.clone());
                }
            }
        }
        "append" => {
            if !json[key].is_array() {
                json[key] = serde_json::json!([]);
            }
            json[key].as_array_mut().unwrap().push(parsed_value);
        }
        "unique" => {
            if !json[key].is_array() {
                json[key] = serde_json::json!([]);
            }
            let arr = json[key].as_array_mut().unwrap();
            if !arr.contains(&parsed_value) {
                arr.push(parsed_value);
            }
        }
        _ => return Err(format!("Unknown strategy: {}", strategy)),
    }

    fs::write(path, json.to_string())
        .map_err(|e| format!("Failed to write JSON file '{}': {}", path, e))
}

pub fn fs_delete(path: &str, key: &str) -> Result<(), String> {
    let content =
        fs::read_to_string(path).map_err(|e| format!("Failed to read '{}': {}", path, e))?;

    let mut json: serde_json::Value =
        serde_json::from_str(&content).map_err(|e| format!("Invalid JSON in '{}': {}", path, e))?;

    if let Some(obj) = json.as_object_mut() {
        obj.remove(key);
    }

    fs::write(path, json.to_string())
        .map_err(|e| format!("Failed to write JSON file '{}': {}", path, e))
}

pub fn fs_array_append(path: &str, key: &str, value: &str) -> Result<(), String> {
    let content = fs::read_to_string(path).unwrap_or_else(|_| "{}".to_string());

    let mut json: serde_json::Value =
        serde_json::from_str(&content).map_err(|e| format!("Invalid JSON in '{}': {}", path, e))?;

    if !json[key].is_array() {
        json[key] = serde_json::json!([]);
    }

    let parsed_value: serde_json::Value = serde_json::from_str(value)
        .unwrap_or_else(|_| serde_json::Value::String(value.to_string()));

    json[key].as_array_mut().unwrap().push(parsed_value);

    fs::write(path, json.to_string())
        .map_err(|e| format!("Failed to write JSON file '{}': {}", path, e))
}

pub fn fs_merge(path: &str, json_str: &str) -> Result<(), String> {
    let file_content = fs::read_to_string(path).unwrap_or_else(|_| "{}".to_string());

    let mut file_json: serde_json::Value = serde_json::from_str(&file_content)
        .map_err(|e| format!("Invalid JSON in '{}': {}", path, e))?;

    let merge_json: serde_json::Value =
        serde_json::from_str(json_str).map_err(|e| format!("Invalid JSON to merge: {}", e))?;

    deep_merge(&mut file_json, merge_json);

    fs::write(path, file_json.to_string())
        .map_err(|e| format!("Failed to write JSON file '{}': {}", path, e))
}

fn deep_merge(target: &mut serde_json::Value, source: serde_json::Value) {
    match (target, source) {
        (serde_json::Value::Object(target_map), serde_json::Value::Object(source_map)) => {
            for (key, value) in source_map {
                if target_map.contains_key(&key) {
                    deep_merge(&mut target_map[&key], value);
                } else {
                    target_map.insert(key, value);
                }
            }
        }
        (target_ref, source_ref) => {
            *target_ref = source_ref;
        }
    }
}