act_lib/
act_patch.rs

1use std::{fs, path::PathBuf, str::FromStr};
2
3use crate::{
4    act_structs::{
5        get_js_constructor_from_acttype, get_ts_type_from_acttype,
6        get_typeinfo_operator_from_acttype, ParamAct, PatchAct, TypeAct,
7    },
8    args_parser::ActArgs,
9    patch_index_helper::PatchIndexHelper,
10};
11use clap::{Parser, ValueEnum};
12
13#[derive(Debug, Clone, ValueEnum)]
14pub enum PatchType {
15    Warning,
16    Error,
17    Fix,
18}
19impl FromStr for PatchType {
20    type Err = String;
21
22    fn from_str(s: &str) -> Result<Self, Self::Err> {
23        match s {
24            "warning" => Ok(PatchType::Warning),
25            "error" => Ok(PatchType::Error),
26            "fix" => Ok(PatchType::Fix),
27            _ => Err(String::from("Invalid enum value")),
28        }
29    }
30}
31
32pub fn gen_param_type_check_patch(
33    param: ParamAct,
34    symbol_name: &String,
35    file_name: &String,
36) -> String {
37    let args = ActArgs::parse();
38    let patch_type = args.patch_type;
39    let param_ts_type = get_ts_type_from_acttype(&param.act_type);
40    let param_js_constructor = get_js_constructor_from_acttype(&param.act_type);
41    let log_message = format!(
42        r#"`[{}=>{}] {} isn't of type {} but of type ${{typeof {}}}`"#,
43        file_name, symbol_name, param.name, param_ts_type, param.name
44    );
45    let patch_body = match patch_type {
46        PatchType::Fix => format!(
47            r#"console.warn({}," and was casted"); {}({});"#,
48            log_message, param_js_constructor, param.name
49        ),
50        PatchType::Error => format!(r#"throw new TypeError({});"#, log_message),
51        PatchType::Warning => format!(r#"console.warn({});"#, log_message),
52    };
53    let typeinfo_operator = get_typeinfo_operator_from_acttype(&param.act_type);
54
55    let conditional_string = match typeinfo_operator.as_str() {
56        "instanceof" => format!(
57            r#"!({} {} {})"#,
58            param.name, typeinfo_operator, param_ts_type
59        ),
60        "typeof" => format!(
61            r#"{} {} !== '{}'"#,
62            typeinfo_operator, param.name, param_ts_type
63        ),
64        _ => "true".to_string(),
65    };
66    let patch_string = format!(
67        r#"
68    if({}){{
69    {}
70    }}
71    "#,
72        conditional_string, patch_body
73    );
74    patch_string
75}
76
77pub fn get_function_param_patch(
78    param: ParamAct,
79    body_start: u32,
80    symbol_name: &String,
81    file_name: &String,
82) -> PatchAct {
83    let patch_string = gen_param_type_check_patch(param, symbol_name, file_name);
84    return PatchAct {
85        byte_pos: body_start,
86        patch: patch_string.as_bytes().to_vec(),
87    };
88}
89
90pub fn get_function_params_patches(
91    params: Vec<ParamAct>,
92    body_start: u32,
93    symbol_name: String,
94    file_name: String,
95) -> Vec<PatchAct> {
96    let mut params_patches: Vec<PatchAct> = vec![];
97    for param in params
98        .into_iter()
99        .filter(|x| x.act_type != TypeAct::Unknown)
100    {
101        params_patches.push(get_function_param_patch(
102            param,
103            body_start,
104            &symbol_name,
105            &file_name,
106        ));
107    }
108    params_patches
109}
110
111pub fn apply_patches(patches: Vec<PatchAct>, file_path: PathBuf) -> Result<(), String> {
112    let mut buffer = fs::read(&file_path).unwrap_or_default();
113    let mut patch_index_helper = PatchIndexHelper::new();
114    for patch in patches {
115        let pos: usize = patch_index_helper.get_drifted_index(patch.byte_pos) as usize;
116        let patch_len: u32 = patch.patch.len() as u32;
117        buffer.splice(pos..pos, patch.patch);
118        patch_index_helper.register_patched_index(patch.byte_pos, patch_len)
119    }
120    let args = ActArgs::parse();
121    let out_folder_path = args.out_folder_path;
122    let in_folder_path = args.folder_path;
123
124    let mut patched_file_path = file_path.clone();
125    // remove the first
126    patched_file_path = patched_file_path
127        .strip_prefix(&in_folder_path)
128        .unwrap()
129        .to_path_buf();
130    patched_file_path = PathBuf::from(out_folder_path).join(patched_file_path);
131    let patch_file_path_without_filename = patched_file_path
132        .parent()
133        .unwrap_or_else(|| {
134            println!("Fail to get parent of {:?}", patched_file_path);
135            panic!("Fail to get parent of {:?}", patched_file_path);
136        })
137        .to_path_buf();
138    fs::create_dir_all(patch_file_path_without_filename).unwrap_or_else(|err| {
139        println!("{:?}", err);
140        panic!("Fail to create out_folder_path");
141    });
142    fs::write(patched_file_path, buffer).unwrap();
143    Ok(())
144}