@sjcrh/proteinpaint-rust 2.192.0 → 2.195.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +0 -8
- package/index.js +2 -164
- package/package.json +1 -1
- package/src/gdcGRIN2.rs +0 -1261
- package/src/gdcmaf.rs +0 -318
package/src/gdcmaf.rs
DELETED
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
This script download cohort maf files from GDC, concatenate them into a single file that includes user specified columns.
|
|
3
|
-
|
|
4
|
-
Input JSON:
|
|
5
|
-
host: GDC host
|
|
6
|
-
fileIdLst: An array of uuid
|
|
7
|
-
headers: required headers for GDC API
|
|
8
|
-
Output gzip compressed maf file to stdout.
|
|
9
|
-
|
|
10
|
-
Example of usage:
|
|
11
|
-
headers='{"X-Forwarded-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.…ML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0", "X-Forwarded-For": "127.0.0.1"}'
|
|
12
|
-
echo '{"host": "https://api.gdc.cancer.gov/data/","columns": ["Hugo_Symbol", "Entrez_Gene_Id", "Center", "NCBI_Build", "Chromosome", "Start_Position"], "fileIdLst": ["8b31d6d1-56f7-4aa8-b026-c64bafd531e7", "b429fcc1-2b59-4b4c-a472-fb27758f6249"], "headers": '$headers'}'|./target/release/gdcmaf
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
use flate2::Compression;
|
|
16
|
-
use flate2::read::GzDecoder;
|
|
17
|
-
use flate2::write::GzEncoder;
|
|
18
|
-
use futures::StreamExt;
|
|
19
|
-
use serde_json::Value;
|
|
20
|
-
use std::io::{self, Read, Write};
|
|
21
|
-
use std::sync::{Arc, Mutex};
|
|
22
|
-
use std::time::Duration;
|
|
23
|
-
use tokio::io::{AsyncReadExt, BufReader};
|
|
24
|
-
use tokio::time::timeout;
|
|
25
|
-
|
|
26
|
-
// Struct to hold error information
|
|
27
|
-
#[derive(serde::Serialize)]
|
|
28
|
-
struct ErrorEntry {
|
|
29
|
-
url: String,
|
|
30
|
-
error: String,
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
fn select_maf_col(d: String, columns: &Vec<String>, url: &str) -> Result<(Vec<u8>, i32), (String, String)> {
|
|
34
|
-
let mut maf_str: String = String::new();
|
|
35
|
-
let mut header_indices: Vec<usize> = Vec::new();
|
|
36
|
-
let lines = d.trim_end().split("\n");
|
|
37
|
-
let mut mafrows = 0;
|
|
38
|
-
for line in lines {
|
|
39
|
-
if line.starts_with("#") {
|
|
40
|
-
continue;
|
|
41
|
-
} else if line.contains("Hugo_Symbol") {
|
|
42
|
-
let header: Vec<String> = line.split("\t").map(|s| s.to_string()).collect();
|
|
43
|
-
for col in columns {
|
|
44
|
-
match header.iter().position(|x| x == col) {
|
|
45
|
-
Some(index) => {
|
|
46
|
-
header_indices.push(index);
|
|
47
|
-
}
|
|
48
|
-
None => {
|
|
49
|
-
let error_msg = format!("Column {} was not found", col);
|
|
50
|
-
return Err((url.to_string(), error_msg));
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
if header_indices.is_empty() {
|
|
55
|
-
return Err((url.to_string(), "No matching columns found".to_string()));
|
|
56
|
-
}
|
|
57
|
-
} else {
|
|
58
|
-
let maf_cont_lst: Vec<String> = line.split("\t").map(|s| s.to_string()).collect();
|
|
59
|
-
let mut maf_out_lst: Vec<String> = Vec::new();
|
|
60
|
-
for x in header_indices.iter() {
|
|
61
|
-
maf_out_lst.push(maf_cont_lst[*x].to_string());
|
|
62
|
-
}
|
|
63
|
-
maf_str.push_str(maf_out_lst.join("\t").as_str());
|
|
64
|
-
maf_str.push_str("\n");
|
|
65
|
-
mafrows += 1;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
Ok((maf_str.as_bytes().to_vec(), mafrows))
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
#[tokio::main]
|
|
72
|
-
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
73
|
-
// Accepting the piped input json from jodejs and assign to the variable
|
|
74
|
-
// host: GDC host
|
|
75
|
-
// url: urls to download single maf files
|
|
76
|
-
let timeout_duration = Duration::from_secs(5); // Set a 10-second timeout
|
|
77
|
-
|
|
78
|
-
// Wrap the read operation in a timeout
|
|
79
|
-
let result = timeout(timeout_duration, async {
|
|
80
|
-
let mut buffer = String::new(); // Initialize an empty string to store input
|
|
81
|
-
let mut reader = BufReader::new(tokio::io::stdin()); // Create a buffered reader for stdin
|
|
82
|
-
reader.read_to_string(&mut buffer).await?; // Read a line asynchronously
|
|
83
|
-
Ok::<String, io::Error>(buffer) // Return the input as a Result
|
|
84
|
-
})
|
|
85
|
-
.await;
|
|
86
|
-
// Handle the result of the timeout operation
|
|
87
|
-
let file_id_lst_js: Value = match result {
|
|
88
|
-
Ok(Ok(buffer)) => match serde_json::from_str(&buffer) {
|
|
89
|
-
Ok(js) => js,
|
|
90
|
-
Err(e) => {
|
|
91
|
-
let stdin_error = ErrorEntry {
|
|
92
|
-
url: String::new(),
|
|
93
|
-
error: format!("JSON parsing error: {}", e),
|
|
94
|
-
};
|
|
95
|
-
writeln!(io::stderr(), "{}", serde_json::to_string(&stdin_error).unwrap()).unwrap();
|
|
96
|
-
return Err(Box::new(std::io::Error::new(
|
|
97
|
-
std::io::ErrorKind::InvalidInput,
|
|
98
|
-
"JSON parsing error!",
|
|
99
|
-
)) as Box<dyn std::error::Error>);
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
Ok(Err(_e)) => {
|
|
103
|
-
let stdin_error = ErrorEntry {
|
|
104
|
-
url: String::new(),
|
|
105
|
-
error: "Error reading from stdin.".to_string(),
|
|
106
|
-
};
|
|
107
|
-
let stdin_error_js = serde_json::to_string(&stdin_error).unwrap();
|
|
108
|
-
writeln!(io::stderr(), "{}", stdin_error_js).expect("Failed to output stderr!");
|
|
109
|
-
return Err(Box::new(std::io::Error::new(
|
|
110
|
-
std::io::ErrorKind::InvalidInput,
|
|
111
|
-
"Failed to output stderr!",
|
|
112
|
-
)) as Box<dyn std::error::Error>);
|
|
113
|
-
}
|
|
114
|
-
Err(_) => {
|
|
115
|
-
let stdin_error = ErrorEntry {
|
|
116
|
-
url: String::new(),
|
|
117
|
-
error: "Timeout while reading from stdin.".to_string(),
|
|
118
|
-
};
|
|
119
|
-
let stdin_error_js = serde_json::to_string(&stdin_error).unwrap();
|
|
120
|
-
writeln!(io::stderr(), "{}", stdin_error_js).expect("Failed to output stderr!");
|
|
121
|
-
return Err(Box::new(std::io::Error::new(
|
|
122
|
-
std::io::ErrorKind::InvalidInput,
|
|
123
|
-
"The columns in arg is not an array",
|
|
124
|
-
)) as Box<dyn std::error::Error>);
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
// reading the input from PP
|
|
129
|
-
let host = file_id_lst_js
|
|
130
|
-
.get("host")
|
|
131
|
-
.expect("Host was not provided")
|
|
132
|
-
.as_str()
|
|
133
|
-
.expect("Host is not a string");
|
|
134
|
-
let mut url: Vec<String> = Vec::new();
|
|
135
|
-
let file_id_lst = file_id_lst_js
|
|
136
|
-
.get("fileIdLst")
|
|
137
|
-
.expect("File ID list is missed!")
|
|
138
|
-
.as_array()
|
|
139
|
-
.expect("File ID list is not an array");
|
|
140
|
-
for v in file_id_lst {
|
|
141
|
-
//url.push(Path::new(&host).join(&v.as_str().unwrap()).display().to_string());
|
|
142
|
-
url.push(format!("{}/{}", host.trim_end_matches('/'), v.as_str().unwrap()));
|
|
143
|
-
}
|
|
144
|
-
let mut req_headers = reqwest::header::HeaderMap::new();
|
|
145
|
-
if let Some(headers_val) = file_id_lst_js.get("headers") {
|
|
146
|
-
let headers_obj = match headers_val.as_object() {
|
|
147
|
-
Some(obj) => obj,
|
|
148
|
-
None => {
|
|
149
|
-
let header_error = ErrorEntry {
|
|
150
|
-
url: String::new(),
|
|
151
|
-
error: "headers is not an object".to_string(),
|
|
152
|
-
};
|
|
153
|
-
let header_error_js = serde_json::to_string(&header_error).unwrap();
|
|
154
|
-
writeln!(io::stderr(), "{}", header_error_js).expect("Failed to output stderr!");
|
|
155
|
-
return Err(Box::new(std::io::Error::new(
|
|
156
|
-
std::io::ErrorKind::InvalidInput,
|
|
157
|
-
"headers is not an object",
|
|
158
|
-
)) as Box<dyn std::error::Error>);
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
for (key, value) in headers_obj {
|
|
162
|
-
req_headers.insert(
|
|
163
|
-
reqwest::header::HeaderName::from_bytes(key.as_bytes()).expect("Invalid header key"),
|
|
164
|
-
reqwest::header::HeaderValue::from_str(value.as_str().expect("Invalid string value"))
|
|
165
|
-
.expect("Invalid header value"),
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// read columns as array from input json and convert data type from Vec<Value> to Vec<String>
|
|
171
|
-
let maf_col: Vec<String>;
|
|
172
|
-
if let Some(maf_col_value) = file_id_lst_js.get("columns") {
|
|
173
|
-
//convert Vec<Value> to Vec<String>
|
|
174
|
-
if let Some(maf_col_array) = maf_col_value.as_array() {
|
|
175
|
-
maf_col = maf_col_array
|
|
176
|
-
.iter()
|
|
177
|
-
.map(|v| v.to_string().replace("\"", ""))
|
|
178
|
-
.collect::<Vec<String>>();
|
|
179
|
-
} else {
|
|
180
|
-
let column_error = ErrorEntry {
|
|
181
|
-
url: String::new(),
|
|
182
|
-
error: "The columns in arg is not an array".to_string(),
|
|
183
|
-
};
|
|
184
|
-
let column_error_js = serde_json::to_string(&column_error).unwrap();
|
|
185
|
-
writeln!(io::stderr(), "{}", column_error_js).expect("Failed to output stderr!");
|
|
186
|
-
return Err(Box::new(std::io::Error::new(
|
|
187
|
-
std::io::ErrorKind::InvalidInput,
|
|
188
|
-
"The columns in arg is not an array",
|
|
189
|
-
)) as Box<dyn std::error::Error>);
|
|
190
|
-
}
|
|
191
|
-
} else {
|
|
192
|
-
let column_error = ErrorEntry {
|
|
193
|
-
url: String::new(),
|
|
194
|
-
error: "Columns was not selected".to_string(),
|
|
195
|
-
};
|
|
196
|
-
let column_error_js = serde_json::to_string(&column_error).unwrap();
|
|
197
|
-
writeln!(io::stderr(), "{}", column_error_js).expect("Failed to output stderr!");
|
|
198
|
-
return Err(Box::new(std::io::Error::new(
|
|
199
|
-
std::io::ErrorKind::InvalidInput,
|
|
200
|
-
"Columns was not selected",
|
|
201
|
-
)) as Box<dyn std::error::Error>);
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
//downloading maf files parallelly and merge them into single maf file
|
|
205
|
-
let download_futures = futures::stream::iter(url.into_iter().map(|url| {
|
|
206
|
-
let req_headers_clone = req_headers.clone();
|
|
207
|
-
async move {
|
|
208
|
-
let client = reqwest::Client::builder()
|
|
209
|
-
.timeout(Duration::from_secs(60)) // 60-second timeout per request
|
|
210
|
-
.connect_timeout(Duration::from_secs(15))
|
|
211
|
-
.default_headers(req_headers_clone.clone())
|
|
212
|
-
.build()
|
|
213
|
-
.map_err(|_e| {
|
|
214
|
-
let client_error = ErrorEntry {
|
|
215
|
-
url: url.clone(),
|
|
216
|
-
error: "Client build error".to_string(),
|
|
217
|
-
};
|
|
218
|
-
let client_error_js = serde_json::to_string(&client_error).unwrap();
|
|
219
|
-
writeln!(io::stderr(), "{}", client_error_js).expect("Failed to build reqwest client!");
|
|
220
|
-
});
|
|
221
|
-
match client.unwrap().get(&url).send().await {
|
|
222
|
-
Ok(resp) if resp.status().is_success() => match resp.bytes().await {
|
|
223
|
-
Ok(content) => {
|
|
224
|
-
let mut decoder = GzDecoder::new(&content[..]);
|
|
225
|
-
let mut decompressed_content = Vec::new();
|
|
226
|
-
match decoder.read_to_end(&mut decompressed_content) {
|
|
227
|
-
Ok(_) => {
|
|
228
|
-
let text = String::from_utf8_lossy(&decompressed_content).to_string();
|
|
229
|
-
return Ok((url.clone(), text));
|
|
230
|
-
}
|
|
231
|
-
Err(e) => {
|
|
232
|
-
let error_msg = format!("Failed to decompress downloaded MAF file: {}", e);
|
|
233
|
-
Err((url.clone(), error_msg))
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
Err(e) => {
|
|
238
|
-
let error_msg = format!("Failed to decompress downloaded MAF file: {}", e);
|
|
239
|
-
Err((url.clone(), error_msg))
|
|
240
|
-
}
|
|
241
|
-
},
|
|
242
|
-
Ok(resp) => {
|
|
243
|
-
let error_msg = format!("HTTP error: {}", resp.status());
|
|
244
|
-
Err((url.clone(), error_msg))
|
|
245
|
-
}
|
|
246
|
-
Err(e) => {
|
|
247
|
-
let error_msg = format!("Server request failed: {}", e);
|
|
248
|
-
Err((url.clone(), error_msg))
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}));
|
|
253
|
-
|
|
254
|
-
// binary output
|
|
255
|
-
let encoder = Arc::new(Mutex::new(GzEncoder::new(io::stdout(), Compression::default())));
|
|
256
|
-
|
|
257
|
-
// Write the header
|
|
258
|
-
{
|
|
259
|
-
let mut encoder_guard = encoder.lock().unwrap(); // Lock the Mutex to get access to the inner GzEncoder
|
|
260
|
-
encoder_guard
|
|
261
|
-
.write_all(&maf_col.join("\t").as_bytes().to_vec())
|
|
262
|
-
.expect("Failed to write header");
|
|
263
|
-
encoder_guard.write_all(b"\n").expect("Failed to write newline");
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
download_futures
|
|
267
|
-
.buffer_unordered(20)
|
|
268
|
-
.for_each(|result| {
|
|
269
|
-
let encoder = Arc::clone(&encoder); // Clone the Arc for each task
|
|
270
|
-
let maf_col_cp = maf_col.clone();
|
|
271
|
-
async move {
|
|
272
|
-
match result {
|
|
273
|
-
Ok((url, content)) => match select_maf_col(content, &maf_col_cp, &url) {
|
|
274
|
-
Ok((maf_bit, mafrows)) => {
|
|
275
|
-
if mafrows > 0 {
|
|
276
|
-
let mut encoder_guard = encoder.lock().unwrap();
|
|
277
|
-
encoder_guard.write_all(&maf_bit).expect("Failed to write file");
|
|
278
|
-
} else {
|
|
279
|
-
let error = ErrorEntry {
|
|
280
|
-
url: url.clone(),
|
|
281
|
-
error: "Empty MAF file".to_string(),
|
|
282
|
-
};
|
|
283
|
-
let error_js = serde_json::to_string(&error).unwrap();
|
|
284
|
-
writeln!(io::stderr(), "{}", error_js).expect("Failed to output stderr!");
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
Err((url, error)) => {
|
|
288
|
-
let error = ErrorEntry { url, error };
|
|
289
|
-
let error_js = serde_json::to_string(&error).unwrap();
|
|
290
|
-
writeln!(io::stderr(), "{}", error_js).expect("Failed to output stderr!");
|
|
291
|
-
}
|
|
292
|
-
},
|
|
293
|
-
Err((url, error)) => {
|
|
294
|
-
let error = ErrorEntry { url, error };
|
|
295
|
-
let error_js = serde_json::to_string(&error).unwrap();
|
|
296
|
-
writeln!(io::stderr(), "{}", error_js).expect("Failed to output stderr!");
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
})
|
|
301
|
-
.await;
|
|
302
|
-
|
|
303
|
-
// Finalize output
|
|
304
|
-
|
|
305
|
-
// Replace the value inside the Mutex with a dummy value (e.g., None)
|
|
306
|
-
let mut encoder_guard = encoder.lock().unwrap();
|
|
307
|
-
let encoder = std::mem::replace(
|
|
308
|
-
&mut *encoder_guard,
|
|
309
|
-
GzEncoder::new(io::stdout(), Compression::default()),
|
|
310
|
-
);
|
|
311
|
-
// Finalize the encoder
|
|
312
|
-
encoder.finish().expect("Maf file output error!");
|
|
313
|
-
|
|
314
|
-
// Manually flush stdout and stderr
|
|
315
|
-
io::stdout().flush().expect("Failed to flush stdout");
|
|
316
|
-
io::stderr().flush().expect("Failed to flush stderr");
|
|
317
|
-
Ok(())
|
|
318
|
-
}
|