1use std::path::{Path, PathBuf};
2
3use chromiumoxide_cdp::cdp::browser_protocol::network::ResourceType;
4
5pub(crate) async fn write<P: AsRef<Path> + Unpin, C: AsRef<[u8]>>(
7 path: P,
8 contents: C,
9) -> std::io::Result<()> {
10 crate::uring_fs::write_file(
11 path.as_ref().display().to_string(),
12 contents.as_ref().to_vec(),
13 )
14 .await
15}
16
17pub(crate) async fn canonicalize<P: AsRef<Path> + Unpin>(path: P) -> std::io::Result<PathBuf> {
22 let path = tokio::fs::canonicalize(path.as_ref()).await?;
23
24 Ok(dunce::simplified(&path).to_path_buf())
25}
26
27pub(crate) fn absolute(path: PathBuf) -> std::io::Result<PathBuf> {
30 let path = if path.is_absolute() {
31 path
32 } else {
33 std::env::current_dir()?.join(path)
34 };
35 Ok(dunce::simplified(&path).to_path_buf())
36}
37
38pub(crate) async fn canonicalize_except_snap(path: PathBuf) -> std::io::Result<PathBuf> {
41 let executable_cleaned: PathBuf = canonicalize(&path).await?;
43
44 Ok(
46 if executable_cleaned
47 .to_str()
48 .map_or(false, |s| s.ends_with("/snap"))
49 {
50 absolute(path)?
51 } else {
52 executable_cleaned
53 },
54 )
55}
56
57pub mod base64 {
58 use base64::engine::general_purpose::STANDARD;
59 use base64::{DecodeError, Engine};
60
61 pub fn decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, DecodeError> {
63 STANDARD.decode(input)
64 }
65}
66
67pub fn evaluation_string(function: impl AsRef<str>, params: &[impl AsRef<str>]) -> String {
70 let params = params
71 .iter()
72 .map(|s| format!("\"{}\"", s.as_ref()))
73 .collect::<Vec<_>>()
74 .join(",");
75 format!("({})({params})", function.as_ref())
76}
77
78pub fn is_likely_js_function(function: impl AsRef<str>) -> bool {
80 let mut fun = function.as_ref().trim_start();
81 if fun.is_empty() {
82 return false;
83 }
84 let mut offset = 0;
85
86 if fun.starts_with("async ") {
87 offset = "async ".len() - 1
88 }
89
90 if fun[offset..].trim_start().starts_with("function ") {
91 return true;
92 } else if skip_args(&mut fun) {
93 if fun.trim_start().starts_with("=>") {
96 return true;
97 }
98 }
99 false
100}
101
102fn skip_args(input: &mut &str) -> bool {
107 if !input.starts_with('(') {
108 return false;
109 }
110 let mut open = 1;
111 let mut closed = 0;
112 *input = &input[1..];
113 while !input.is_empty() && open != closed {
114 if let Some(idx) = input.find(&['(', ')'] as &[_]) {
115 if &input[idx..=idx] == ")" {
116 closed += 1;
117 } else {
118 open += 1;
119 }
120 *input = &input[idx + 1..];
121 } else {
122 break;
123 }
124 }
125
126 open == closed
127}
128
129pub fn is_data_resource(resource_type: &ResourceType) -> bool {
131 matches!(
132 resource_type,
133 ResourceType::Xhr
134 | ResourceType::Fetch
135 | ResourceType::WebSocket
136 | ResourceType::EventSource
137 | ResourceType::Prefetch
138 | ResourceType::Preflight
139 )
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn is_js_function() {
148 assert!(is_likely_js_function("function abc() {}"));
149 assert!(is_likely_js_function("async function abc() {}"));
150 assert!(is_likely_js_function("() => {}"));
151 assert!(is_likely_js_function("(abc, def) => {}"));
152 assert!(is_likely_js_function("((abc), (def)) => {}"));
153 assert!(is_likely_js_function("() => Promise.resolve(100 / 25)"));
154 }
155}