💻
YewでバイナリをDnDしてパースして表示する
はじめに
こう言う感じのやつをYewで作りたいとします。
先行事例
(先にこちらの2つのリンク先を読んでいただくと分かりやすいです)
こちらのissueではファイルのDnDの方法が記載されています。
こちらの記事では(DnDではなく)入力されたバイナリをパースしています。
2番目の記事がやりたいことにかなり近いのですが、結局do_something
に何を書けば良いのか、と言う問題がありました。
と言うのは、ファイルをパースしてもそれをどうにかして表示しなければならないわけです。
しかしパースしたものをどうやってonload
関数の「外」に持っていくかが問題です。単純にonloadの外にlet mut
しておくと所有権絡みでエラーになりますし、そもそもパースした構造体がcopyできないと言う制約もありました(この構造体はライブラリのものです)。
そこで、パースする前のbytes: Vec<u8>
を(Reactで言う)setStateして、表示する前にパースしました。
#[function_component(App)]
pub fn app() -> Html {
let node = use_node_ref();
let state = use_drop(node.clone());
let bin = use_state(|| None);
として、
// ここまでかなり省略
{if let Some(files) = &*state.files {
html! {for files.iter().map(|file| {
let file_reader = web_sys::FileReader::new().unwrap();
file_reader.read_as_array_buffer(&file).unwrap();
let bin_cloned = bin.clone(); // moveされるためclone
let onload = Closure::wrap(Box::new(move |event: Event| {
let file_reader: FileReader = event.target().unwrap().dyn_into().unwrap();
let file = file_reader.result().unwrap();
let file = js_sys::Uint8Array::new(&file);
let mut bytes = vec![0; file.length() as usize];
file.copy_to(&mut bytes);
bin_cloned.set(Some(bytes));
}) as Box<dyn FnMut(_)>);
file_reader.set_onload(Some(onload.as_ref().unchecked_ref()));
onload.forget();
とします。
後は使う側で、
let op_pe = parse(&*bin); // ここで*binをパース
if let Some(pe) = op_pe {
html! {
<>
<DosTable signature={pe.header.dos_header.signature} pe_pointer={pe.header.dos_header.pe_pointer}/> // この辺は適当に自分で作ったコンポーネント
<CoffTable machine={pe.header.coff_header.machine} number_of_sections={pe.header.coff_header.number_of_sections}/>
</>
}
} else {
html! {
// 省略
}
}
として、DnDされたバイナリファイルをパースできました。ここはCustom Hookを使うともっと良くなると思います(が、まだ作りかけなのでご勘弁を。。。)。
おわりに
やはりRust製のライブラリ(ここではバイナリのパーサー)を使うとき、UIまで一気通貫に作れるYewは良いです。
Discussion