refactor: simplify code, use format!, more idiomatic code

This commit is contained in:
Richard Ivánek 2024-09-26 11:07:10 +02:00
parent 5025e49e61
commit 369b94e7e3
7 changed files with 117 additions and 118 deletions

33
Cargo.lock generated
View File

@ -394,6 +394,12 @@ version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.3.9" version = "0.3.9"
@ -533,6 +539,8 @@ dependencies = [
"regex", "regex",
"reqwest", "reqwest",
"rquickjs", "rquickjs",
"strum",
"strum_macros",
"tokio", "tokio",
"tokio-util", "tokio-util",
"tub", "tub",
@ -969,6 +977,12 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54"
[[package]]
name = "rustversion"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.17" version = "1.0.17"
@ -1090,6 +1104,25 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "strum"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
[[package]]
name = "strum_macros"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.60" version = "2.0.60"

View File

@ -16,6 +16,8 @@ tokio-util = { version = "0.7.10", features=["futures-io", "futures-util", "code
futures = "0.3.30" futures = "0.3.30"
log = "0.4.22" log = "0.4.22"
env_logger = "0.11.5" env_logger = "0.11.5"
strum = "0.26.3"
strum_macros = "0.26.4"
# Compilation optimizations for release builds # Compilation optimizations for release builds
# Increases compile time but typically produces a faster and smaller binary. Suitable for final releases but not for debug builds. # Increases compile time but typically produces a faster and smaller binary. Suitable for final releases but not for debug builds.

View File

@ -25,4 +25,4 @@ pub static REGEX_SIGNATURE_FUNCTION: &Lazy<Regex> =
pub static REGEX_HELPER_OBJ_NAME: &Lazy<Regex> = regex!(";([A-Za-z0-9_\\$]{2,})\\...\\("); pub static REGEX_HELPER_OBJ_NAME: &Lazy<Regex> = regex!(";([A-Za-z0-9_\\$]{2,})\\...\\(");
pub static NSIG_FUNCTION_NAME: &str = "decrypt_nsig"; pub static NSIG_FUNCTION_NAME: &str = "decrypt_nsig";
pub static SIG_FUNCTION_NAME: &str = "decrypt_sig"; pub static _SIG_FUNCTION_NAME: &str = "decrypt_sig";

View File

@ -1,46 +1,27 @@
use futures::SinkExt; use futures::SinkExt;
use log::{debug, error};
use rquickjs::{async_with, AsyncContext, AsyncRuntime}; use rquickjs::{async_with, AsyncContext, AsyncRuntime};
use std::{num::NonZeroUsize, sync::Arc, thread::available_parallelism, time::SystemTime}; use std::{num::NonZeroUsize, sync::Arc, thread::available_parallelism, time::SystemTime};
use log::{debug, error}; use strum_macros::{Display, FromRepr};
use tokio::{runtime::Handle, sync::Mutex, task::block_in_place}; use tokio::{runtime::Handle, sync::Mutex, task::block_in_place};
use tub::Pool; use tub::Pool;
use crate::{consts::NSIG_FUNCTION_NAME, opcode::OpcodeResponse, player::fetch_update}; use crate::{consts::NSIG_FUNCTION_NAME, opcode::OpcodeResponse, player::fetch_update};
#[derive(Display, FromRepr)]
pub enum JobOpcode { pub enum JobOpcode {
ForceUpdate, ForceUpdate = 0,
DecryptNSignature, DecryptNSignature = 1,
DecryptSignature, DecryptSignature,
GetSignatureTimestamp, GetSignatureTimestamp,
PlayerStatus, PlayerStatus,
PlayerUpdateTimestamp, PlayerUpdateTimestamp,
UnknownOpcode, UnknownOpcode = 255,
} }
impl std::fmt::Display for JobOpcode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ForceUpdate => write!(f, "ForceUpdate"),
Self::DecryptNSignature => write!(f, "DecryptNSignature"),
Self::DecryptSignature => write!(f, "DecryptSignature"),
Self::GetSignatureTimestamp => write!(f, "GetSignatureTimestamp"),
Self::PlayerStatus => write!(f, "PlayerStatus"),
Self::PlayerUpdateTimestamp => write!(f, "PlayerUpdateTimestamp"),
Self::UnknownOpcode => write!(f, "UnknownOpcode"),
}
}
}
impl From<u8> for JobOpcode { impl From<u8> for JobOpcode {
fn from(value: u8) -> Self { fn from(value: u8) -> Self {
match value { JobOpcode::from_repr(value as usize).unwrap_or(Self::UnknownOpcode)
0x00 => Self::ForceUpdate,
0x01 => Self::DecryptNSignature,
0x02 => Self::DecryptSignature,
0x03 => Self::GetSignatureTimestamp,
0x04 => Self::PlayerStatus,
0x05 => Self::PlayerUpdateTimestamp,
_ => Self::UnknownOpcode,
}
} }
} }
@ -54,7 +35,22 @@ pub struct PlayerInfo {
pub last_update: SystemTime, pub last_update: SystemTime,
} }
impl Default for PlayerInfo {
fn default() -> Self {
Self {
nsig_function_code: Default::default(),
sig_function_code: Default::default(),
sig_function_name: Default::default(),
signature_timestamp: Default::default(),
player_id: Default::default(),
has_player: Default::default(),
last_update: SystemTime::now(),
}
}
}
pub struct JavascriptInterpreter { pub struct JavascriptInterpreter {
#[allow(dead_code)]
js_runtime: AsyncRuntime, js_runtime: AsyncRuntime,
sig_context: AsyncContext, sig_context: AsyncContext,
nsig_context: AsyncContext, nsig_context: AsyncContext,
@ -65,17 +61,20 @@ pub struct JavascriptInterpreter {
impl JavascriptInterpreter { impl JavascriptInterpreter {
pub fn new() -> JavascriptInterpreter { pub fn new() -> JavascriptInterpreter {
let js_runtime = AsyncRuntime::new().unwrap(); let js_runtime = AsyncRuntime::new().unwrap();
// not ideal, but this is only done at startup // not ideal, but this is only done at startup
let nsig_context = block_in_place(|| { let nsig_context = block_in_place(|| {
Handle::current() Handle::current()
.block_on(AsyncContext::full(&js_runtime)) .block_on(AsyncContext::full(&js_runtime))
.unwrap() .unwrap()
}); });
let sig_context = block_in_place(|| { let sig_context = block_in_place(|| {
Handle::current() Handle::current()
.block_on(AsyncContext::full(&js_runtime)) .block_on(AsyncContext::full(&js_runtime))
.unwrap() .unwrap()
}); });
JavascriptInterpreter { JavascriptInterpreter {
js_runtime, js_runtime,
sig_context, sig_context,
@ -98,22 +97,16 @@ impl GlobalState {
.get(); .get();
let mut runtime_vector: Vec<Arc<JavascriptInterpreter>> = let mut runtime_vector: Vec<Arc<JavascriptInterpreter>> =
Vec::with_capacity(number_of_runtimes); Vec::with_capacity(number_of_runtimes);
for _n in 0..number_of_runtimes {
for _ in 0..number_of_runtimes {
runtime_vector.push(Arc::new(JavascriptInterpreter::new())); runtime_vector.push(Arc::new(JavascriptInterpreter::new()));
} }
let runtime_pool: Pool<Arc<JavascriptInterpreter>> = Pool::from_vec(runtime_vector); let js_runtime_pool: Pool<Arc<JavascriptInterpreter>> = Pool::from_vec(runtime_vector);
GlobalState { GlobalState {
player_info: Mutex::new(PlayerInfo { player_info: Mutex::new(PlayerInfo::default()),
nsig_function_code: Default::default(), js_runtime_pool,
sig_function_code: Default::default(),
sig_function_name: Default::default(),
player_id: Default::default(),
signature_timestamp: Default::default(),
has_player: 0x00,
last_update: SystemTime::now(),
}),
js_runtime_pool: runtime_pool,
} }
} }
} }
@ -183,11 +176,7 @@ pub async fn process_decrypt_n_signature<W>(
} }
drop(player_info); drop(player_info);
let mut call_string: String = String::new(); let call_string = format!("{NSIG_FUNCTION_NAME}(\"{}\")", sig.replace("\"", "\\\""));
call_string += NSIG_FUNCTION_NAME;
call_string += "(\"";
call_string += &sig.replace("\"", "\\\"");
call_string += "\")";
let decrypted_string = match ctx.eval::<String,String>(call_string.clone()) { let decrypted_string = match ctx.eval::<String,String>(call_string.clone()) {
Ok(x) => x, Ok(x) => x,
@ -263,11 +252,7 @@ pub async fn process_decrypt_signature<W>(
let sig_function_name = &player_info.sig_function_name; let sig_function_name = &player_info.sig_function_name;
let mut call_string: String = String::new(); let call_string = format!("{sig_function_name}(\"{}\")", sig.replace("\"", "\\\""));
call_string += sig_function_name;
call_string += "(\"";
call_string += &sig.replace("\"", "\\\"");
call_string += "\")";
drop(player_info); drop(player_info);

View File

@ -5,11 +5,12 @@ mod player;
use ::futures::StreamExt; use ::futures::StreamExt;
use consts::{DEFAULT_SOCK_PATH, DEFAULT_TCP_URL}; use consts::{DEFAULT_SOCK_PATH, DEFAULT_TCP_URL};
use env_logger::Env;
use jobs::{process_decrypt_n_signature, process_fetch_update, GlobalState, JobOpcode}; use jobs::{process_decrypt_n_signature, process_fetch_update, GlobalState, JobOpcode};
use log::{debug, error, info};
use opcode::OpcodeDecoder; use opcode::OpcodeDecoder;
use player::fetch_update; use player::fetch_update;
use std::{env::args, sync::Arc}; use std::{env::args, sync::Arc};
use env_logger::Env;
use tokio::{ use tokio::{
fs::remove_file, fs::remove_file,
io::{AsyncReadExt, AsyncWrite}, io::{AsyncReadExt, AsyncWrite},
@ -17,7 +18,6 @@ use tokio::{
sync::Mutex, sync::Mutex,
}; };
use tokio_util::codec::Framed; use tokio_util::codec::Framed;
use log::{info, error, debug};
use crate::jobs::{ use crate::jobs::{
process_decrypt_signature, process_get_signature_timestamp, process_player_status, process_decrypt_signature, process_get_signature_timestamp, process_player_status,
@ -30,7 +30,7 @@ macro_rules! loop_main {
match fetch_update($s.clone()).await { match fetch_update($s.clone()).await {
Ok(()) => info!("Successfully fetched player"), Ok(()) => info!("Successfully fetched player"),
Err(x) => { Err(x) => {
error!("Error occured while trying to fetch the player: {:?}", x); error!("Error occurred while trying to fetch the player: {:?}", x);
} }
} }
loop { loop {
@ -48,19 +48,14 @@ async fn main() {
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
let args: Vec<String> = args().collect(); let args: Vec<String> = args().collect();
let socket_url: &str = match args.get(1) { let socket_url: &str = args.get(1).map(String::as_ref).unwrap_or(DEFAULT_SOCK_PATH);
Some(stringref) => stringref,
None => DEFAULT_SOCK_PATH,
};
// have to please rust // have to please rust
let state: Arc<GlobalState> = Arc::new(GlobalState::new()); let state: Arc<GlobalState> = Arc::new(GlobalState::new());
if socket_url == "--tcp" { if socket_url == "--tcp" {
let socket_tcp_url: &str = match args.get(2) { let socket_tcp_url = args.get(2).map(String::as_ref).unwrap_or(DEFAULT_TCP_URL);
Some(stringref) => stringref,
None => DEFAULT_TCP_URL,
};
let tcp_socket = match TcpListener::bind(socket_tcp_url).await { let tcp_socket = match TcpListener::bind(socket_tcp_url).await {
Ok(x) => x, Ok(x) => x,
Err(x) => { Err(x) => {
@ -68,27 +63,29 @@ async fn main() {
return; return;
} }
}; };
loop_main!(tcp_socket, state); loop_main!(tcp_socket, state);
} else if socket_url == "--test" { } else if socket_url == "--test" {
// TODO: test the API aswell, this only tests the player script extractor // TODO: test the API as well, this only tests the player script extractor
info!("Fetching player"); info!("Fetching player");
match fetch_update(state.clone()).await {
Ok(()) => std::process::exit(0), std::process::exit(match fetch_update(state.clone()).await {
Err(_x) => std::process::exit(-1), Ok(_) => 0,
} Err(_) => -1,
});
} else { } else {
let unix_socket = match UnixListener::bind(socket_url) { let unix_socket = match UnixListener::bind(socket_url) {
Ok(x) => x, Ok(x) => x,
Err(x) if x.kind() == std::io::ErrorKind::AddrInUse => {
let _ = remove_file(socket_url).await;
UnixListener::bind(socket_url).unwrap()
}
Err(x) => { Err(x) => {
if x.kind() == std::io::ErrorKind::AddrInUse { error!("Error occurred while trying to bind: {}", x);
remove_file(socket_url).await; return;
UnixListener::bind(socket_url).unwrap()
} else {
error!("Error occurred while trying to bind: {}", x);
return;
}
} }
}; };
loop_main!(unix_socket, state); loop_main!(unix_socket, state);
} }
} }
@ -108,18 +105,17 @@ where
Ok(opcode) => { Ok(opcode) => {
debug!("Received job: {}", opcode.opcode); debug!("Received job: {}", opcode.opcode);
let cloned_state = state.clone();
let cloned_sink = arc_sink.clone();
match opcode.opcode { match opcode.opcode {
JobOpcode::ForceUpdate => { JobOpcode::ForceUpdate => {
let cloned_state = state.clone();
let cloned_sink = arc_sink.clone();
tokio::spawn(async move { tokio::spawn(async move {
process_fetch_update(cloned_state, cloned_sink, opcode.request_id) process_fetch_update(cloned_state, cloned_sink, opcode.request_id)
.await; .await;
}); });
} }
JobOpcode::DecryptNSignature => { JobOpcode::DecryptNSignature => {
let cloned_state = state.clone();
let cloned_sink = arc_sink.clone();
tokio::spawn(async move { tokio::spawn(async move {
process_decrypt_n_signature( process_decrypt_n_signature(
cloned_state, cloned_state,
@ -131,8 +127,6 @@ where
}); });
} }
JobOpcode::DecryptSignature => { JobOpcode::DecryptSignature => {
let cloned_state = state.clone();
let cloned_sink = arc_sink.clone();
tokio::spawn(async move { tokio::spawn(async move {
process_decrypt_signature( process_decrypt_signature(
cloned_state, cloned_state,
@ -144,8 +138,6 @@ where
}); });
} }
JobOpcode::GetSignatureTimestamp => { JobOpcode::GetSignatureTimestamp => {
let cloned_state = state.clone();
let cloned_sink = arc_sink.clone();
tokio::spawn(async move { tokio::spawn(async move {
process_get_signature_timestamp( process_get_signature_timestamp(
cloned_state, cloned_state,
@ -156,16 +148,12 @@ where
}); });
} }
JobOpcode::PlayerStatus => { JobOpcode::PlayerStatus => {
let cloned_state = state.clone();
let cloned_sink = arc_sink.clone();
tokio::spawn(async move { tokio::spawn(async move {
process_player_status(cloned_state, cloned_sink, opcode.request_id) process_player_status(cloned_state, cloned_sink, opcode.request_id)
.await; .await;
}); });
} }
JobOpcode::PlayerUpdateTimestamp => { JobOpcode::PlayerUpdateTimestamp => {
let cloned_state = state.clone();
let cloned_sink = arc_sink.clone();
tokio::spawn(async move { tokio::spawn(async move {
process_player_update_timestamp( process_player_update_timestamp(
cloned_state, cloned_state,

View File

@ -1,5 +1,5 @@
use std::io::ErrorKind;
use log::debug; use log::debug;
use std::io::ErrorKind;
use tokio_util::{ use tokio_util::{
bytes::{Buf, BufMut}, bytes::{Buf, BufMut},
codec::{Decoder, Encoder}, codec::{Decoder, Encoder},

View File

@ -1,6 +1,6 @@
use std::{sync::Arc, time::SystemTime};
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use regex::Regex; use regex::Regex;
use std::{sync::Arc, time::SystemTime};
use crate::{ use crate::{
consts::{ consts::{
@ -64,19 +64,20 @@ pub async fn fetch_update(state: Arc<GlobalState>) -> Result<(), FetchUpdateStat
let mut nsig_function_array_opt = None; let mut nsig_function_array_opt = None;
// Extract nsig function array code // Extract nsig function array code
for (index, nsig_function_array_str) in NSIG_FUNCTION_ARRAYS.iter().enumerate() { for (index, nsig_function_array_str) in NSIG_FUNCTION_ARRAYS.iter().enumerate() {
let nsig_function_array_regex = Regex::new(&nsig_function_array_str).unwrap(); let nsig_function_array_regex = Regex::new(nsig_function_array_str).unwrap();
nsig_function_array_opt = match nsig_function_array_regex.captures(&player_javascript) { nsig_function_array_opt = match nsig_function_array_regex.captures(&player_javascript) {
None => { None => {
warn!("nsig function array did not work: {}", nsig_function_array_str); warn!(
"nsig function array did not work: {}",
nsig_function_array_str
);
if index == NSIG_FUNCTION_ARRAYS.len() { if index == NSIG_FUNCTION_ARRAYS.len() {
error!("!!ERROR!! nsig function array unable to be extracted"); error!("!!ERROR!! nsig function array unable to be extracted");
return Err(FetchUpdateStatus::NsigRegexCompileFailed); return Err(FetchUpdateStatus::NsigRegexCompileFailed);
} }
continue; continue;
} }
Some(i) => { Some(i) => Some(i),
Some(i)
}
}; };
break; break;
} }
@ -90,10 +91,10 @@ pub async fn fetch_update(state: Arc<GlobalState>) -> Result<(), FetchUpdateStat
.parse::<usize>() .parse::<usize>()
.unwrap(); .unwrap();
let mut nsig_array_context_regex: String = String::new(); let nsig_array_context_regex: String = format!(
nsig_array_context_regex += "var "; "var {name}\\s*=\\s*\\[(.+?)][;,]",
nsig_array_context_regex += &nsig_array_name.replace("$", "\\$"); name = nsig_array_name.replace("$", "\\$")
nsig_array_context_regex += "\\s*=\\s*\\[(.+?)][;,]"; );
let nsig_array_context = match Regex::new(&nsig_array_context_regex) { let nsig_array_context = match Regex::new(&nsig_array_context_regex) {
Ok(x) => x, Ok(x) => x,
@ -115,20 +116,19 @@ pub async fn fetch_update(state: Arc<GlobalState>) -> Result<(), FetchUpdateStat
let nsig_function_name = array_values.get(nsig_array_value).unwrap(); let nsig_function_name = array_values.get(nsig_array_value).unwrap();
let mut nsig_function_code = String::new(); let mut nsig_function_code = format!("function {NSIG_FUNCTION_NAME}");
nsig_function_code += "function ";
nsig_function_code += NSIG_FUNCTION_NAME;
// Extract nsig function code // Extract nsig function code
for (index, ending) in NSIG_FUNCTION_ENDINGS.iter().enumerate() { for (index, ending) in NSIG_FUNCTION_ENDINGS.iter().enumerate() {
let mut nsig_function_code_regex_str: String = String::new(); let nsig_function_code_regex_str =
nsig_function_code_regex_str += &nsig_function_name.replace("$", "\\$"); format!("{}{ending}", nsig_function_name.replace("$", "\\$"));
nsig_function_code_regex_str += ending;
let nsig_function_code_regex = Regex::new(&nsig_function_code_regex_str).unwrap(); let nsig_function_code_regex = Regex::new(&nsig_function_code_regex_str).unwrap();
nsig_function_code += match nsig_function_code_regex.captures(&player_javascript) { nsig_function_code += match nsig_function_code_regex.captures(&player_javascript) {
None => { None => {
warn!("nsig function ending did not work: {}", ending); warn!("nsig function ending did not work: {}", ending);
if index == NSIG_FUNCTION_ENDINGS.len() { if index == NSIG_FUNCTION_ENDINGS.len() {
error!("!!ERROR!! nsig function unable to be extracted"); error!("!!ERROR!! nsig function unable to be extracted");
return Err(FetchUpdateStatus::NsigRegexCompileFailed); return Err(FetchUpdateStatus::NsigRegexCompileFailed);
@ -136,9 +136,7 @@ pub async fn fetch_update(state: Arc<GlobalState>) -> Result<(), FetchUpdateStat
continue; continue;
} }
Some(i) => { Some(i) => i.get(1).unwrap().as_str(),
i.get(1).unwrap().as_str()
}
}; };
debug!("got nsig fn code: {}", nsig_function_code); debug!("got nsig fn code: {}", nsig_function_code);
break; break;
@ -152,9 +150,10 @@ pub async fn fetch_update(state: Arc<GlobalState>) -> Result<(), FetchUpdateStat
.unwrap() .unwrap()
.as_str(); .as_str();
let mut sig_function_body_regex_str: String = String::new(); let sig_function_body_regex_str = format!(
sig_function_body_regex_str += &sig_function_name.replace("$", "\\$"); "{name}=function\\([a-zA-Z0-9_]+\\)\\{{.+?\\}}",
sig_function_body_regex_str += "=function\\([a-zA-Z0-9_]+\\)\\{.+?\\}"; name = sig_function_name.replace("$", "\\$")
);
let sig_function_body_regex = Regex::new(&sig_function_body_regex_str).unwrap(); let sig_function_body_regex = Regex::new(&sig_function_body_regex_str).unwrap();
@ -173,10 +172,8 @@ pub async fn fetch_update(state: Arc<GlobalState>) -> Result<(), FetchUpdateStat
.unwrap() .unwrap()
.as_str(); .as_str();
let mut helper_object_body_regex_str = String::new(); let helper_object_body_regex_str =
helper_object_body_regex_str += "(var "; format!("(var {helper_object_name}=\\{{(?:.|\\n)+?\\}}\\}};)");
helper_object_body_regex_str += helper_object_name;
helper_object_body_regex_str += "=\\{(?:.|\\n)+?\\}\\};)";
let helper_object_body_regex = Regex::new(&helper_object_body_regex_str).unwrap(); let helper_object_body_regex = Regex::new(&helper_object_body_regex_str).unwrap();
let helper_object_body = helper_object_body_regex let helper_object_body = helper_object_body_regex
@ -186,13 +183,7 @@ pub async fn fetch_update(state: Arc<GlobalState>) -> Result<(), FetchUpdateStat
.unwrap() .unwrap()
.as_str(); .as_str();
let mut sig_code = String::new(); let sig_code = format!("var {sig_function_name};{helper_object_body}{sig_function_body}");
sig_code += "var ";
sig_code += sig_function_name;
sig_code += ";";
sig_code += helper_object_body;
sig_code += sig_function_body;
info!("sig code: {}", sig_code); info!("sig code: {}", sig_code);