From 601b0e2aa030693082c58db957c38e67f25d300f Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Sat, 1 Jun 2019 22:59:27 +0200 Subject: Initial version --- .gitignore | 3 + COPYING | 21 +++ Cargo.lock | 437 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 25 ++++ README.md | 24 ++++ src/db.rs | 258 +++++++++++++++++++++++++++++++++++ src/main.rs | 69 ++++++++++ src/table.rs | 235 ++++++++++++++++++++++++++++++++ 8 files changed, 1072 insertions(+) create mode 100644 .gitignore create mode 100644 COPYING create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/db.rs create mode 100644 src/main.rs create mode 100644 src/table.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..421b147 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +/db diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..9cd9159 --- /dev/null +++ b/COPYING @@ -0,0 +1,21 @@ +This project is licensed under the MIT license. + +Copyright (C) 2019 Nicolas Schodet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..eee2d38 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,437 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "adler32" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "aho-corasick" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atty" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "build_const" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cc" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "csv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "csv-core" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "either" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "flate2" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itertools" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz-sys" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz_oxide" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz_oxide_c_api" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", + "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "numtoa" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pinmap" +version = "0.1.0" +dependencies = [ + "csv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "roxmltree 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "roxmltree" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "xmlparser 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ryu" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "structopt" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "structopt-derive" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.15.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "termion" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", + "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ucd-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-segmentation" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-width" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "utf8-ranges" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "xmlparser" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" +"checksum aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c" +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" +"checksum cc 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a0c56216487bb80eec9c4516337b2588a4f2a2290d72a1416d930e4dcdb0c90d" +"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" +"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +"checksum csv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9044e25afb0924b5a5fc5511689b0918629e85d68ea591e5e87fbf1e85ea1b3b" +"checksum csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5cdef62f37e6ffe7d1f07a381bc0db32b7a3ff1cac0de56cb0d81e71f53d65" +"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" +"checksum flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f87e68aa82b2de08a6e037f1385455759df6e445a8df5e005b4297191dbf18aa" +"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" +"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" +"checksum libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)" = "c6785aa7dd976f5fbf3b71cfd9cd49d7f783c1ff565a858d71031c6c313aa5c6" +"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" +"checksum miniz-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0300eafb20369952951699b68243ab4334f4b10a88f411c221d444b36c40e649" +"checksum miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c468f2369f07d651a5d0bb2c9079f8488a66d5466efe42d0c5c6466edcb7f71e" +"checksum miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7fe927a42e3807ef71defb191dc87d4e24479b221e67015fe38ae2b7b447bab" +"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" +"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" +"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f0a0bcab2fd7d1d7c54fa9eae6f43eddeb9ce2e7352f8518a814a4f65d60c58" +"checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96" +"checksum roxmltree 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "53b0200cbfa8b3f6cfd6076592717d697a1ddc57cb2a8fbfd3d133c06011b579" +"checksum ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f" +"checksum serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "a72e9b96fa45ce22a4bc23da3858dfccfd60acd28a25bcd328a98fdd6bea43fd" +"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +"checksum structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "3d0760c312538987d363c36c42339b55f5ee176ea8808bbe4543d484a291c8d1" +"checksum structopt-derive 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "528aeb7351d042e6ffbc2a6fb76a86f9b622fdf7c25932798e7a82cb03bc94c6" +"checksum syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)" = "a1393e4a97a19c01e900df2aec855a29f71cf02c402e2f443b8d2747c25c5dbe" +"checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" +"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" +"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" +"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum xmlparser 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ecec95f00fb0ff019153e64ea520f87d1409769db3e8f4db3ea588638a3e1cee" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9030e37 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "pinmap" +version = "0.1.0" +authors = ["Nicolas Schodet "] +description = """ +pinmap is a tool to help pin mapping when designing a board with an STM32 +microcontroller. It uses a database extracted from CubeMX to construct a +table of all pins and associated signals. This table can be open using your +favorite spreadsheet to assign functions to pins. +""" +homepage = "http://git.ni.fr.eu.org/nicolas/pinmap.git/about/" +repository = "http://git.ni.fr.eu.org/nicolas/pinmap.git/" +readme = "README.md" +keywords = ["embedded", "stm32"] +categories = ["command-line-utilities", "embedded"] +license = "MIT" +edition = "2018" + +[dependencies] +csv = "1.0.7" +flate2 = "1.0" +itertools = "0.8.0" +regex = "1" +roxmltree = "0.6" +structopt = "0.2" diff --git a/README.md b/README.md new file mode 100644 index 0000000..dba9bbd --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +pinmap +------ + +pinmap is a tool to help pin mapping when designing a board with an STM32 +microcontroller. + +It uses a database extracted from CubeMX to construct a table of all pins and +associated signals. This table can be open using your favorite spreadsheet to +assign functions to pins. + +pinmap supports GPIO mapping using AF (alternate functions) and the old system +using REMAP. The output table format differ to accommodate the differences. + +### Extracting the database + +Download CubeMX from st.com, and run the installation. In the installed +directory, there is a db directory, copy this to pinmap directory. + +To reduce disk usage, pinmap uses a compressed database, run the following +command to compress it: + +``` +find . -exec gzip '{}' + +``` diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..41ef85e --- /dev/null +++ b/src/db.rs @@ -0,0 +1,258 @@ +// Copyright (C) 2019 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software +// and associated documentation files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +//! This module handles loading parts information from database. +use flate2::read::GzDecoder; +use roxmltree::{Document, Node}; +use std::collections::HashMap; +use std::error::Error; +use std::fs::File; +use std::io::Read; +use std::path::Path; + +static EXT: &str = ".xml.gz"; + +type Result = std::result::Result>; + +/// Information about a part. +#[derive(Debug)] +pub struct PartInfo<'a> { + /// Part. + pub part: &'a str, + /// Product line. + pub line: String, + /// Package. + pub package: String, + /// GPIO mapping mode. + pub gpio_mode: GpioMode, + /// Information for all pins. + pub pins: Vec, +} + +/// Information about one pin. +#[derive(Debug)] +pub struct PinInfo { + /// Name. + pub name: String, + /// Position in package. This can be a number or a letter with a number. + pub position: String, + /// Signals. + pub signals: Vec, +} + +/// Information about one signal. +#[derive(Debug)] +pub struct SignalInfo { + /// Name. + pub name: String, + /// Mapping information. + pub map: SignalMap, +} + +/// Information on how to map a signal to a pin. +#[derive(Clone, Debug)] +pub enum SignalMap { + /// Alternate function, with its AF number. + AF(u8), + /// Additional function, no AF setup to do. + AddF, + /// Remap, used on older parts without the AF system. The signal can be available on several + /// remaps. + Remap(Vec), +} + +/// Mode of GPIO mapping. +#[derive(Clone, Copy, Debug)] +pub enum GpioMode { + /// Alternate function based mapping. + AF, + /// Remap based mapping. + Remap, +} + +/// Map pins and signals to mapping information. This is used temporarily when loading it from a +/// separated file. +type GpiosInfo = HashMap>; + +impl<'a> PartInfo<'a> { + /// Extract information from XML file in database. + pub fn new(database: &Path, part: &'a str) -> Result> { + // Read XML. + let xml_name = database.join(["mcu/", part, EXT].concat()); + let xml = read_gziped(&xml_name)?; + let doc = Document::parse(&xml)?; + let doc_root = doc.root_element(); + // Basic attributes. + let line = attribute_or_error(&doc_root, "Line")?; + let package = attribute_or_error(&doc_root, "Package")?; + // GPIO. + let gpio_ip = doc_root + .children() + .find(|n| n.has_tag_name("IP") && n.attribute("Name") == Some("GPIO")) + .ok_or("missing GPIO")?; + let gpio_version = attribute_or_error(&gpio_ip, "Version")?; + let (gpio_mode, gpios_info) = load_gpios(database, &gpio_version)?; + // Pins. + fn parse_signal( + signals_map: Option<&HashMap>, + s: Node, + ) -> Result { + let name = attribute_or_error(&s, "Name")?; + let map = match signals_map { + None => SignalMap::AddF, + Some(signals_map) => signals_map.get(&name).unwrap_or(&SignalMap::AddF).clone(), + }; + Ok(SignalInfo { name, map }) + }; + fn parse_pin(gpios_info: &GpiosInfo, n: Node) -> Result { + let name = attribute_or_error(&n, "Name")?; + let position = attribute_or_error(&n, "Position")?; + let signals = n + .children() + .filter(|s| s.has_tag_name("Signal") && s.attribute("Name") != Some("GPIO")) + .map(|s| { + let signals_map = gpios_info.get(&name); + parse_signal(signals_map, s) + }) + .collect::>()?; + Ok(PinInfo { + name, + position, + signals, + }) + }; + let pins = doc_root + .children() + .filter(|n| n.has_tag_name("Pin")) + .map(|n| parse_pin(&gpios_info, n)) + .collect::>()?; + // Done. + Ok(PartInfo { + part, + line, + package, + gpio_mode, + pins, + }) + } + /// Produce a one-line part summary. + pub fn summary(self: &Self) -> String { + format!("{}: {} {}", self.part, self.line, self.package) + } +} + +/// List all parts in database matching a given regex. +pub fn list_parts(database: &Path, pattern: &str) -> Result> { + let re = regex::Regex::new(pattern)?; + let mut list = Vec::new(); + for entry in database.join("mcu").read_dir()? { + if let Some(name) = entry?.file_name().to_str() { + if name.ends_with(EXT) { + let part = &name[..(name.len() - EXT.len())]; + if re.is_match(part) { + list.push(part.to_owned()); + } + } + } + } + Ok(list) +} + +/// Load information on GPIOs from XML file in database. Return a hash indexed by pin and signal, +/// giving signal mapping information. +fn load_gpios(database: &Path, gpio_version: &str) -> Result<(GpioMode, GpiosInfo)> { + // Read XML. + let xml_name = database.join(["mcu/IP/GPIO-", gpio_version, "_Modes", EXT].concat()); + let xml = read_gziped(&xml_name)?; + let doc = Document::parse(&xml)?; + let doc_root = doc.root_element(); + // Decode document. + fn parse_af(signal: Node) -> Result { + let af = signal + .descendants() + .find(|n| n.has_tag_name("PossibleValue")) + .ok_or("no AF found")? + .text() + .ok_or("no AF text")?; + let k = "GPIO_AF"; + if !af.starts_with(k) { + return Err("not an AF".into()); + } + let i = af[k.len()..].find('_').ok_or("not an AF")?; + let af = &af[k.len()..k.len() + i]; + let af = af.parse::()?; + Ok(SignalMap::AF(af)) + } + fn parse_remaps(signal: Node) -> Result { + let remap_blocks = signal.children().filter(|n| n.has_tag_name("RemapBlock")); + fn parse_remap(n: Node) -> Result { + let name = attribute_or_error(&n, "Name")?; + let k = "REMAP"; + let i = name.rfind(k).ok_or("missing REMAP")?; + let remap = name[i + k.len()..].parse::()?; + Ok(remap) + } + let remaps = remap_blocks.map(parse_remap).collect::>()?; + Ok(SignalMap::Remap(remaps)) + } + let mut gpios = HashMap::new(); + let pins = doc_root.children().filter(|n| n.has_tag_name("GPIO_Pin")); + let mut mode = None; + for pin in pins { + let pin_name = attribute_or_error(&pin, "Name")?; + let signals = pin.children().filter(|n| n.has_tag_name("PinSignal")); + let mut signals_map = HashMap::new(); + for signal in signals { + // First try to parse a remap until this fails once. + if mode.is_none() { + let one_remap_block = signal.children().find(|n| n.has_tag_name("RemapBlock")); + mode = if one_remap_block.is_some() { + Some(GpioMode::Remap) + } else { + Some(GpioMode::AF) + } + } + let map = match mode.unwrap() { + GpioMode::AF => parse_af(signal), + GpioMode::Remap => parse_remaps(signal), + }?; + let signal_name = attribute_or_error(&signal, "Name")?; + signals_map.insert(signal_name, map); + } + gpios.insert(pin_name, signals_map); + } + Ok((mode.unwrap_or(GpioMode::AF), gpios)) +} + +/// Read gziped file to string. +fn read_gziped(path: &Path) -> Result { + let mut gunzip = GzDecoder::new(File::open(path)?); + let mut xml = String::new(); + gunzip.read_to_string(&mut xml)?; + Ok(xml) +} + +/// Factorize attribute getter, return an error if not found. +fn attribute_or_error(node: &Node, name: &str) -> Result { + match node.attribute(name) { + Some(v) => Ok(v.to_owned()), + None => { + let tag = node.tag_name().name(); + Err(format!("{} missing a {} attribute", tag, name).into()) + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..3ac9d28 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,69 @@ +// Copyright (C) 2019 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software +// and associated documentation files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +//! Help with pin assignment on STM32. +//! +//! This reads database extracted from CubeMX and produce a table of all signals that can be mapped +//! to the microcontroller pins. This table can be open with a spreadsheet. +use std::error::Error; +use std::io; +use std::path::PathBuf; +use structopt::StructOpt; + +mod db; +mod table; + +/// MCU pins mapper. +#[derive(StructOpt, Debug)] +struct Opt { + /// Database path + #[structopt(short = "d", long, default_value = "db", parse(from_os_str))] + database: PathBuf, + /// Exclude component + #[structopt(short = "x", long, number_of_values = 1)] + exclude: Vec, + #[structopt(subcommand)] + command: OptCommand, +} + +#[derive(StructOpt, Debug)] +enum OptCommand { + /// Search the database for MCUs matching the given regex. + #[structopt(name = "parts")] + Parts { pattern: String }, + /// Output a pin out table for a given part. + #[structopt(name = "table")] + Table { part: String }, +} + +fn main() -> Result<(), Box> { + let opt = Opt::from_args(); + match opt.command { + OptCommand::Parts { pattern } => { + for part in db::list_parts(&opt.database, &pattern)? { + let part_info = db::PartInfo::new(&opt.database, &part)?; + println!("{}", part_info.summary()); + } + } + OptCommand::Table { part } => { + let part_info = db::PartInfo::new(&opt.database, &part)?; + let filter = table::SignalFilter::new(&opt.exclude)?; + table::write_pin_out(&part_info, io::stdout(), &filter)?; + } + } + Ok(()) +} diff --git a/src/table.rs b/src/table.rs new file mode 100644 index 0000000..efdc23d --- /dev/null +++ b/src/table.rs @@ -0,0 +1,235 @@ +// Copyright (C) 2019 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software +// and associated documentation files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +//! Handle table output. +use crate::db; +use itertools::Itertools; +use regex::{Regex, RegexSet}; +use std::collections::hash_set::HashSet; +use std::collections::HashMap; +use std::error::Error; +use std::io::Write; +use std::result::Result as StdResult; + +type Result = StdResult>; + +/// Filter signals to reduce pin out table size. +pub struct SignalFilter { + /// Signals to exclude from table. + excludes: RegexSet, + /// Substitutions to shorten signal names. + subs: Vec, + /// Factorizations to reduce the number of similar signals, with the associated separator. + facts_sep: Vec<(Regex, &'static str)>, +} + +/// Produce a pin out table. +pub fn write_pin_out( + part_info: &db::PartInfo, + writer: impl Write, + filter: &SignalFilter, +) -> Result<()> { + match part_info.gpio_mode { + db::GpioMode::AF => write_pin_out_af(part_info, writer, filter), + db::GpioMode::Remap => write_pin_out_remap(part_info, writer, filter), + } +} + +/// Produce a pin out table for AF based parts. +fn write_pin_out_af( + part_info: &db::PartInfo, + writer: impl Write, + filter: &SignalFilter, +) -> Result<()> { + let mut writer = csv::Writer::from_writer(writer); + for pin in &part_info.pins { + let mut signals: [Vec<_>; 17] = Default::default(); + for signal in &pin.signals { + let index = match signal.map { + db::SignalMap::AF(af) => af as usize, + db::SignalMap::AddF => signals.len() - 1, + _ => panic!("Bad signal map"), + }; + signals[index].push(signal.name.as_str()); + } + let signals = filter.signal_filter(&pin.name, &pin.position, &signals); + let mut row = Vec::new(); + row.push(pin.name.clone()); + row.push(pin.position.clone()); + for i in &signals { + row.push(i.join(" ")); + } + writer.write_record(row)?; + } + Ok(()) +} + +/// Produce a pin out table for Remap based parts. +fn write_pin_out_remap( + part_info: &db::PartInfo, + writer: impl Write, + filter: &SignalFilter, +) -> Result<()> { + let mut lines = Vec::new(); + let mut allcats = HashSet::new(); + for pin in &part_info.pins { + let signals = pin + .signals + .iter() + .map(|signal| { + if let db::SignalMap::Remap(remaps) = &signal.map { + let remaps = remaps.iter().sorted().map(|x| x.to_string()).join(","); + format!("{}({})", signal.name, remaps) + } else { + signal.name.clone() + } + }) + .collect::>(); + let signals = filter + .signal_filter(&pin.name, &pin.position, &[signals]) + .into_iter() + .next() + .unwrap(); + let mut signals_hash = HashMap::new(); + for signal in signals { + let cat = signal.split('_').next().unwrap().to_owned(); + allcats.insert(cat.clone()); + signals_hash.entry(cat).or_insert(Vec::new()).push(signal); + } + lines.push((&pin.name, &pin.position, signals_hash)); + } + let mut writer = csv::Writer::from_writer(writer); + let mut allcats = allcats.into_iter().collect::>(); + allcats.sort(); + for (name, position, signals_hash) in lines { + let mut row = Vec::new(); + row.push(name.clone()); + row.push(position.clone()); + for cat in &allcats { + let col = signals_hash.get(cat); + if let Some(col) = col { + row.push(col.iter().join(" ")); + } else { + row.push(String::from("")); + } + } + writer.write_record(row)?; + } + Ok(()) +} + +impl SignalFilter { + /// Prepare a new filter. + pub fn new(exclude: &Vec) -> StdResult { + let excludes = RegexSet::new(exclude.iter().map(|x| format!(r"^(?:{})[0-9_]", x)))?; + let subs = [ + "((?:HR|LP)?T)IM", + "((?:LP)?U)S?ART", + "(D)FSDM", + "(F)S?MC", + "(Q)UADSPI(?:_BK)?", + "(S)PI", + "(SW)PMI", + "I2(S)", + "(SD)MMC", + "(SP)DIFRX", + "FD(C)AN", + "USB_OTG_([FH]S)", + r"(T\d_B)KIN", + ] + .iter() + .map(|x| Regex::new(&format!(r"^{}([0-9_])", x))) + .collect::>()?; + let facts_sep = [ + (r"T\d_B\d?_COMP(\d+)", ""), + (r"ADC(\d)_IN[NP]?\d+", ""), + (r"ADC\d+_IN([NP]?\d+)", ""), + (r"[SUT]\d_(.+)", "/"), + ] + .iter() + .map(|(fact, sep)| Ok((Regex::new(fact)?, *sep))) + .collect::>()?; + Ok(SignalFilter { + excludes, + subs, + facts_sep, + }) + } + /// Filter a list of signal. + fn signal_filter<'a, I, J, S>( + self: &Self, + _name: &str, + _position: &str, + cols: I, + ) -> Vec> + where + S: ToString, + J: IntoIterator, + I: IntoIterator, + { + let mut res = Vec::new(); + for signals in cols { + let signals = signals + .into_iter() + .map(|s| { + self.subs + .iter() + .fold(s.to_string(), |s, re| re.replace(&s, "$1$2").to_string()) + }) + .collect(); + let signals = self.facts_sep.iter().fold(signals, |signals, (fact, sep)| { + factorize(&signals, &fact, sep) + }); + let signals = signals + .into_iter() + .filter(|s| !self.excludes.is_match(s)) + .collect(); + res.push(signals); + } + res + } +} + +/// For a given iterable, match each items with the given regex, if there are several matches they +/// are factorized on the first subgroup. +fn factorize(it: I, re: &Regex, sep: &str) -> Vec +where + S: ToString, + I: IntoIterator, +{ + let mut others = Vec::new(); + let mut facts: HashMap<_, Vec<_>> = HashMap::new(); + for i in it { + let i = i.to_string(); + if let Some(c) = re.captures(&i) { + let g = c.get(1).expect("first group should match"); + let termout = (&i[..g.start()], &i[g.end()..]); + let termout = (termout.0.to_owned(), termout.1.to_owned()); + let term = g.as_str().to_owned(); + facts.entry(termout).or_insert(Vec::new()).push(term); + } else { + others.push(i); + } + } + let mut r = Vec::new(); + for (termout, terms) in facts { + let terms = terms.join(sep); + r.push(format!("{}{}{}", termout.0, terms, termout.1)); + } + r.extend(others); + r +} -- cgit v1.2.3