parsercherがv3.1.0になったよ!
parsercher v3.1.0
前回のリリース記事「Rustでタグをパースするクレートを公開したよ! - 薄っぺらりん」からバージョンアップしてv3.1.0をリリースしました。
いくつかAPIを追加したりしたので今回はその辺を書こうと思います。変更点はgithubのReleasesやCHANGELOG.mdに記載しています。
- crates.io: https://crates.io/crates/parsercher/
- doc.rs: https://docs.rs/parsercher/
目次
部分木の検索ができるようになりました
Dom構造体ツリーから別のDom構造体ツリーを使用して部分木を検索できます。
これにはparsercher::search_dom()
を使用します。
次の例はdoc
からneedle
と一致する部分木を取得するものです。 list1
とlist3
が一致します。
needle:
<ul class="targetList"> <li class="key1"></li> <li class="key2"></li> </ul>
例:
let doc = r#" <body> <ul id="list1" class="targetList"> <li class="key1">1-1</li> <li class="key2"><span>1-2</span></li> </ul> <ul id="list2"> <li class="key1">2-1</li> <li>2-2</li> </ul> <div> <div> <ul class="targetList"> <ul id="list3" class="targetList"> <li class="key1">3-1</li> <li class="item">3-2</li> <li class="key2">3-3</li> </ul> </ul> </div> </div> <ul id="list4"> <li class="key1">4-1</li> <li class="key2">4-2</li> </ul> </body> "#; let doc_dom = parsercher::parse(&doc).unwrap(); let needle = r#" <ul class="targetList"> <li class="key1"></li> <li class="key2"></li> </ul> "#; let needle_dom = parsercher::parse(&needle).unwrap(); // Remove `root`dom of needle_dom let needle_dom = needle_dom.get_children().unwrap().get(0).unwrap(); if let Some(dom) = parsercher::search_dom(&doc_dom, &needle_dom) { parsercher::print_dom_tree(&dom); }
出力:
<root> <ul id="list1" class="targetList"> <li class="key1"> TEXT: "1-1" <li class="key2"> <span> TEXT: "1-2" <ul class="targetList" id="list3"> <li class="key1"> TEXT: "3-1" <li class="item"> TEXT: "3-2" <li class="key2"> TEXT: "3-3"
タグの属性を検索できるようになりました
全てのタグから任意の属性の値を取得できます。
これにはparsercher::search_attr()
を使用します。
複数の属性を指定できるparsercher::search_attrs()
もあります。
例えば、全てのid
属性から値を取得する場合は次のようにします。
let doc = r#" <head> <meta charset="UTF-8"> <meta id="value1"> <title>sample html</title> </head> <body id="value2"> <h1>sample</h1> <div id="value3"></div> <ol> <li>first</li> <li id="value4">second</li> <li>therd</li> </ol> </body> "#; let dom = parsercher::parse(&doc).unwrap(); let values = parsercher::search_attr(&dom, "id").unwrap(); assert_eq!(values.len(), 4); assert_eq!(values[0], "value1".to_string()); assert_eq!(values[1], "value2".to_string()); assert_eq!(values[2], "value3".to_string()); assert_eq!(values[3], "value4".to_string());
id
属性に加えてclass
属性の値も取得する場合は次のようにします。
let doc = r#" <head> <meta charset="UTF-8"> <meta id="id1"> <title>sample html</title> </head> <body id="id2" class="class1"> <h1>sample</h1> <div align="center" class="class2"></div> <ol> <li>first</li> <li class="class3">second</li> <li>therd</li> </ol> </body> "#; let dom = parsercher::parse(&doc).unwrap(); let attrs = vec!["id", "class"]; let values = parsercher::search_attrs(&dom, &attrs).unwrap(); assert_eq!(values.len(), 5); assert_eq!(values[0], "id1".to_string()); assert_eq!(values[1], "id2".to_string()); assert_eq!(values[2], "class1".to_string()); assert_eq!(values[3], "class2".to_string()); assert_eq!(values[4], "class3".to_string());
木の十分条件を評価できるようになりました
木の十分条件を評価するDom::p_implies_q_tree()
を追加しました。
部分木の検索はこれにより実現されています。
また、Dom
構造体の十分条件を評価するDom::p_implies_q()
を追加したほか、以前までタグの十分条件を評価するためにあったparsercher::satisfy_sufficient_condition()
はTag::p_implies_q()
に変更しました。
以下は木の十分条件を評価する例です。
let p = r#" <body> <div></div> <ul> <li class="item"><li> </ul> </body> "#; let p_dom = parsercher::parse(&p).unwrap(); let q = r#" <body> <div id="content"></div> <ul id="liset1"> <li class="item">item1<li> <li class="item">item2<li> <li class="item">item3<li> </ul> </body> "#; let q_dom = parsercher::parse(&q).unwrap(); assert_eq!(Dom::p_implies_q_tree(&p_dom, &q_dom), true);
domモジュール下の構造体が使いやすくなりました
Tag
,Text
,Comment
構造体について、コンストラクタの引数の型がString
から&str
になりました。Tag
構造体の属性設定がより簡単に記述できるようになりました。従来のHashMap
を使用する方法はTag::set_attrs()
として残っています。
// <div id="section" class="alert alert-primary"> let mut tag = Tag::new("div"); tag.set_attr("id", "section"); tag.set_attr("class", "alert alert-primary");
- PartialEqを継承しました。
Dom
構造体も含むため、木を等価演算子で比較できます。
let a = r#" <head> <title>sample</title> </head> <body> <h1>section</h1> <ul> <li>list1</li> <li>list2</li> </ul> </body> "#; let a_dom = parsercher::parse(&a); let b = r#" <head> <title>sample</title> </head> <body> <h1>section</h1> <ul> <li>list1</li> <li>list2</li> </ul> </body> "#; let b_dom = parsercher::parse(&b); assert_eq!(a_dom == b_dom, true); assert_eq!(a_dom != b_dom, false);
まとめ
API追加の合間に既存API名の変更を行っていたので短期間にメジャーバージョンが3まで来ました。思い付きで楽しく作っていて都度リリースしているので、セマンティックバージョニングを採用しているとメジャーバージョンが上がりがちです。crates.ioの他のクレートを見ていると同じことになっているものもあるので、そんなもんかなと思います。数字が大きくなるのはスマートではないけれど別に悪いことではないし、バージョン番号は差異があることを示すための数字に過ぎない気もします。
自分は会社や仕事のことがずっと頭から離れないのですが、最近は「あとはどんな機能があったら便利かな」と考えたりすることもあって、いい気分転換になっている気がします。