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(¶m.act_type);
40 let param_js_constructor = get_js_constructor_from_acttype(¶m.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(¶m.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 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}