use std::path::{Path, PathBuf};
use chromiumoxide_cdp::cdp::browser_protocol::network::ResourceType;
pub(crate) async fn write<P: AsRef<Path> + Unpin, C: AsRef<[u8]>>(
path: P,
contents: C,
) -> std::io::Result<()> {
tokio::fs::write(path.as_ref(), contents.as_ref()).await
}
pub(crate) async fn canonicalize<P: AsRef<Path> + Unpin>(path: P) -> std::io::Result<PathBuf> {
let path = tokio::fs::canonicalize(path.as_ref()).await?;
Ok(dunce::simplified(&path).to_path_buf())
}
pub(crate) fn absolute(path: PathBuf) -> std::io::Result<PathBuf> {
let path = if path.is_absolute() {
path
} else {
std::env::current_dir()?.join(path)
};
Ok(dunce::simplified(&path).to_path_buf())
}
pub(crate) async fn canonicalize_except_snap(path: PathBuf) -> std::io::Result<PathBuf> {
let executable_cleaned: PathBuf = canonicalize(&path).await?;
Ok(if executable_cleaned.to_str().unwrap().ends_with("/snap") {
absolute(path).unwrap()
} else {
executable_cleaned
})
}
pub mod base64 {
use base64::engine::general_purpose::STANDARD;
use base64::{DecodeError, Engine};
pub fn decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, DecodeError> {
STANDARD.decode(input)
}
}
pub fn evaluation_string(function: impl AsRef<str>, params: &[impl AsRef<str>]) -> String {
let params = params
.iter()
.map(|s| format!("\"{}\"", s.as_ref()))
.collect::<Vec<_>>()
.join(",");
format!("({})({params})", function.as_ref())
}
pub fn is_likely_js_function(function: impl AsRef<str>) -> bool {
let mut fun = function.as_ref().trim_start();
if fun.is_empty() {
return false;
}
let mut offset = 0;
if fun.starts_with("async ") {
offset = "async ".len() - 1
}
if fun[offset..].trim_start().starts_with("function ") {
return true;
} else if skip_args(&mut fun) {
if fun.trim_start().starts_with("=>") {
return true;
}
}
false
}
fn skip_args(input: &mut &str) -> bool {
if !input.starts_with('(') {
return false;
}
let mut open = 1;
let mut closed = 0;
*input = &input[1..];
while !input.is_empty() && open != closed {
if let Some(idx) = input.find(&['(', ')'] as &[_]) {
if &input[idx..=idx] == ")" {
closed += 1;
} else {
open += 1;
}
*input = &input[idx + 1..];
} else {
break;
}
}
open == closed
}
pub fn is_data_resource(resource_type: &ResourceType) -> bool {
matches!(
resource_type,
ResourceType::Xhr
| ResourceType::Fetch
| ResourceType::WebSocket
| ResourceType::EventSource
| ResourceType::Prefetch
| ResourceType::Preflight
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_js_function() {
assert!(is_likely_js_function("function abc() {}"));
assert!(is_likely_js_function("async function abc() {}"));
assert!(is_likely_js_function("() => {}"));
assert!(is_likely_js_function("(abc, def) => {}"));
assert!(is_likely_js_function("((abc), (def)) => {}"));
assert!(is_likely_js_function("() => Promise.resolve(100 / 25)"));
}
}