1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use std::{fs, path::PathBuf, str::FromStr};

use crate::{
    act_structs::{
        get_js_constructor_from_acttype, get_ts_type_from_acttype,
        get_typeinfo_operator_from_acttype, ParamAct, PatchAct, TypeAct,
    },
    args_parser::ActArgs,
    patch_index_helper::PatchIndexHelper,
};
use clap::Parser;

#[derive(Debug, Clone)]
pub enum PatchType {
    Warning,
    Error,
    Fix,
}
impl FromStr for PatchType {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "warning" => Ok(PatchType::Warning),
            "error" => Ok(PatchType::Error),
            "fix" => Ok(PatchType::Fix),
            _ => Err(String::from("Invalid enum value")),
        }
    }
}

pub fn gen_param_type_check_patch(param: ParamAct) -> String {
    let args = ActArgs::parse();
    let patch_type = args.patch_type;
    let param_ts_type = get_ts_type_from_acttype(&param.act_type);
    let param_js_constructor = get_js_constructor_from_acttype(&param.act_type);
    let log_message = format!(
        r#"`{} isn't of type {} but of type ${{typeof {}}}`"#,
        param.name, param_ts_type, param.name
    );
    let patch_body = match patch_type {
        PatchType::Fix => format!(
            r#"console.warn({}," and was casted"); {}({});"#,
            log_message, param_js_constructor, param.name
        ),
        PatchType::Error => format!(r#"throw {};"#, log_message),
        PatchType::Warning => format!(r#"console.warn({});"#, log_message),
    };
    let typeinfo_operator = get_typeinfo_operator_from_acttype(&param.act_type);

    let conditional_string = match typeinfo_operator.as_str() {
        "instanceof" => format!(
            r#"!({} {} {})"#,
            param.name, typeinfo_operator, param_ts_type
        ),
        "typeof" => format!(
            r#"{} {} !== '{}'"#,
            typeinfo_operator, param.name, param_ts_type
        ),
        _ => "true".to_string(),
    };
    let patch_string = format!(
        r#"
    if({}){{
    {}
    }}
    "#,
        conditional_string, patch_body
    );
    patch_string
}

pub fn get_function_param_patch(param: ParamAct, body_start: u32) -> PatchAct {
    let patch_string = gen_param_type_check_patch(param);
    return PatchAct {
        byte_pos: body_start,
        patch: patch_string.as_bytes().to_vec(),
    };
}

pub fn get_function_params_patches(params: Vec<ParamAct>, body_start: u32) -> Vec<PatchAct> {
    let mut params_patches: Vec<PatchAct> = vec![];
    for param in params
        .into_iter()
        .filter(|x| x.act_type != TypeAct::Unknown)
    {
        params_patches.push(get_function_param_patch(param, body_start));
    }
    params_patches
}

pub fn apply_patches(patches: Vec<PatchAct>, file_path: PathBuf) -> Result<(), String> {
    let mut buffer = fs::read(&file_path).unwrap_or_default();
    let mut patch_index_helper = PatchIndexHelper::new();
    for patch in patches {
        let pos: usize = patch_index_helper.get_drifted_index(patch.byte_pos) as usize;
        let patch_len: u32 = patch.patch.len() as u32;
        buffer.splice(pos..pos, patch.patch);
        patch_index_helper.register_patched_index(patch.byte_pos, patch_len)
    }
    let args = ActArgs::parse();
    let out_folder_path = args.out_folder_path;
    let in_folder_path = args.folder_path;

    let mut patched_file_path = file_path.clone();
    // remove the first
    patched_file_path = patched_file_path
        .strip_prefix(&in_folder_path)
        .unwrap()
        .to_path_buf();
    patched_file_path = PathBuf::from(out_folder_path).join(patched_file_path);
    let patch_file_path_without_filename = patched_file_path
        .parent()
        .unwrap_or_else(|| {
            println!("Fail to get parent of {:?}", patched_file_path);
            panic!("Fail to get parent of {:?}", patched_file_path);
        })
        .to_path_buf();
    fs::create_dir_all(patch_file_path_without_filename).unwrap_or_else(|err| {
        println!("{:?}", err);
        panic!("Fail to create out_folder_path");
    });
    fs::write(patched_file_path, buffer).unwrap();
    Ok(())
}