rust
XLSX
ods

ExcelやLibreOfficeのspreadsheetのファイルを読み書きするcrateの作成

spsheet

ExcelのxlsxファイルとLibreOffceのodsファイルを読み書きするcrateです。
spsheet
現在セルの内容の読み書きと、一部日付のフォーマットをサポートしています。
xlsxとodsはそれぞれfeatureになっているので好きな方を使ってください。

extern crate spsheet;
use spsheet::ods;
use spsheet::xlsx;
use spsheet::{Book,Sheet,Cell};
use spsheet::style::Style;
use std::path::Path;

let mut book = Book::new();
let mut sheet = Sheet::new("シート1");
sheet.add_cell(Cell::str("a"), 0, 0);
sheet.add_cell(Cell::str("b"), 0, 1);
sheet.add_cell(Cell::float(1.0), 1, 0);
sheet.add_cell(Cell::float(2.0), 1, 1);
sheet.add_cell(Cell::date_with_style("2017-12-02", Style::new("MM\\月DD\\日")), 2, 0);
sheet.add_cell(Cell::date_with_style("2017-12-02T13:30:00", Style::new("YYYY/MM/DD\\ HH:MM:SS")), 2, 1);
book.add_sheet(sheet);

let _ = ods::write(&book, Path::new("./tests/test.ods"));
let res = ods::read(Path::new("./tests/test.ods")).unwrap();
assert_eq!(book, res);

let _ = xlsx::write(&book, Path::new("./tests/test.xlsx"));
let res = xlsx::read(Path::new("./tests/test.xlsx")).unwrap();
assert_eq!(book, res);

TODO

  • フォーマット
    • 日付フォーマットの完成
    • 数値フォーマット
  • セル
    • 高さ
    • 罫線
  • 数式

おまけ

動機

Rustを学習していて、そろそろ何か作りたくなってきたので、AdventCalendar2017を締め切りにして、crateを一つ作っと見ようと思いました。
仕事柄、ExcelやLibreOfficeのファイルを読み書きすることが多かったので、これがRustで使えるようになると色々とはかどりそうです。
crate.ioにもいくつかそういう目的のライブラリがあるんですが、読み書きを同時にできるライブラリがなかったのでSpreadsheetを統合的に扱えるライブラリを目指しました。

Rustの学習

借用やライフタイムのあたりは読むとなんとなくわかるけど、実際に書くとエラーになり微妙な気分になるような、そんな初心者です。このcrateを作るのにモジュールやサブモジュールに切り分けたりして、ガンガン借用が必要になるようにしてみました。関数引数とかclosureとかも積極的にいれてみました。
ただライフタイムはいまいち不安だったので、structにはなるべく参照を持ち込まないようにして、今後習熟してきたら試してみたいと思っています。

xlsxとods

どちらも実態はzip圧縮ファイルで展開すると主に情報は複数のxmlファイルで記述されています。
読みではtempdirに解凍した後、xmlをパースしてtempdirを削除します。
書きではtempdirにxmlファイルをいくつか書き込んんでzip圧縮して目的のファイルに書き込んだのちtempdirを削除しています。
xmlのパースはquick_xmlを使っています。サイトを見ると速度自慢が際立ちます。

日付フォーマット

仕様はこちらExcel のセルの表示形式で [ユーザー定義] に使用できる書式記号について
当初適当に文字列を順番に舐めていけばいいかと思っていたんですが、全然歯が立ちませんでした。
色々ググっていると、そういう目的にはパーサーコンビネータのnomが良さそうだとわかり、これを利用しました。
言語処理系に嗜みがあったのでパーサーコンビネータ自体はさくっと理解できたんですが、nomがRustのマクロ全開なライブラリだったため、この辺りが大変でした。おかげでマクロもなんとなく理解できるようになりました。
いよいよパースできそうだと思っていた時に難敵があらわれました。「m」です。
これは月を意味することもあれば時間を意味することもあります。なんで同じ記号でやろうと思ったんでしょうか・・・
見分けるためには「h」と「s」が「m」の前後にあれば時間と解釈するんだそうです。このおかげてnomが一段階複雑になりました。時間もなくなり数値フォーマットまで手がまわりませんんでした。

Google Spreadsheet

当初このライブラリはExcel, Libreoffice, GoogleSpreadsheetの3つの読み書きができる統合ツールにしようと思っていました。しかしRustでGoogle Spreadsheet APIをサポートしているライブラリが見つからなかったので断念しました。
Google APIのサポートライブラリは結構あるんですがど、なぜかSpreadsheetはありませんでした。他のライブラリを参考にすれば書けるかなと思いましたが、結構なボリュームだったので諦めました。
APIが追加されれば、すぐ取り込みたいところですが、出ないならこちらのライブラリが一通り完成してから、自作しようかなと思っています。Google Spreadsheetで自動化することがあるんでRustで使えるとうれしいんですが・・・

名前

当初名前はspreadsheetにしようと思ってました。crate.ioでxlsxやodsで検索しても出なかったので、てっきり存在しないと持っていました。
で、登録する前にダイレクトに名前を検索したところ残念ながら出てきちゃいました。
その説明がこれ

Agnostic spreadsheet reader and writer (reserved namespace).

1年半くらい放置されてますね。とっとと作れ!もしくは手放せ!
そこで名前は「hyoukeisan」にしようかとも思いましたが今の名前に落ち着きました。

まとめ

色々とRustが身についたような気がします。
まだ借用でエラーがでることもあるけど、すぐに対処法が見えて書き直せるようになりました。
基本的なファイル操作やzipの扱いやxmlの扱いも学べました。
nomのおかげでパーサーコンビネータやらマクロやらもわかりました。

あと書いてて楽しかったですね。
締め切りがあるから、ダラダラせずに書けたのもよかったです。
以降のバージョンアップも何かしらの強制力を付けたいです。

これ書いている間にダウンロードは5回された。ちょっとうれしい。