From 22dc419a3e0bee5d9a7ea72900b8503e9bd014fc Mon Sep 17 00:00:00 2001 From: Zhenhui Xie Date: Tue, 7 Apr 2020 01:16:18 +0800 Subject: [PATCH] improvement: add parser for format strings (#1021) This PR implements the parser of format strings described in #624. --- Cargo.lock | 132 ++++++++++++++++ Cargo.toml | 2 + src/config.rs | 2 +- src/formatter/mod.rs | 5 + src/formatter/model.rs | 17 ++ src/formatter/parser.rs | 76 +++++++++ src/formatter/spec.pest | 16 ++ src/formatter/string_formatter.rs | 252 ++++++++++++++++++++++++++++++ src/lib.rs | 4 + src/main.rs | 3 + src/module.rs | 5 + src/segment.rs | 7 +- 12 files changed, 517 insertions(+), 4 deletions(-) create mode 100644 src/formatter/mod.rs create mode 100644 src/formatter/model.rs create mode 100644 src/formatter/parser.rs create mode 100644 src/formatter/spec.pest create mode 100644 src/formatter/string_formatter.rs diff --git a/Cargo.lock b/Cargo.lock index 6c324643..5c9ff58c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -107,11 +107,40 @@ dependencies = [ "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byte-unit" version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bytes" version = "0.5.4" @@ -230,6 +259,14 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "dirs" version = "2.0.2" @@ -277,6 +314,11 @@ dependencies = [ "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fnv" version = "1.0.6" @@ -295,6 +337,14 @@ name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "gethostname" version = "0.2.1" @@ -455,6 +505,11 @@ dependencies = [ "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "matches" version = "0.1.8" @@ -561,6 +616,11 @@ name = "once_cell" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "open" version = "1.4.0" @@ -628,6 +688,45 @@ name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-trie 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_generator 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_meta 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pkg-config" version = "0.3.17" @@ -876,6 +975,17 @@ dependencies = [ "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "smallvec" version = "1.2.0" @@ -901,6 +1011,8 @@ dependencies = [ "open 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "os_info 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "path-slash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_env_logger 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1031,6 +1143,11 @@ name = "typenum" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-bidi" version = "0.3.4" @@ -1170,7 +1287,11 @@ dependencies = [ "checksum battery 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "36a698e449024a5d18994a815998bf5e2e4bc1883e35a7d7ba95b6b69ee45907" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byte-unit 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6894a79550807490d9f19a138a6da0f8830e70c83e83402dd23f16fd6c479056" +"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" "checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" "checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" @@ -1185,15 +1306,18 @@ dependencies = [ "checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" "checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" "checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" "checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" "checksum doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" "checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum gethostname 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e692e296bfac1d2533ef168d0b60ff5897b8b70a4009276834014dd8924cc028" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" "checksum git2 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ef222034f2069cfc5af01ce423574d3d9a3925bd4052912a14e5bcfd7ca9e47a" @@ -1213,6 +1337,7 @@ dependencies = [ "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" +"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" "checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" @@ -1226,6 +1351,7 @@ dependencies = [ "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" "checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" "checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum open 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c283bf0114efea9e42f1a60edea9859e8c47528eae09d01df4b29c1e489cc48" "checksum openssl 0.10.28 (registry+https://github.com/rust-lang/crates.io-index)" = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" @@ -1234,6 +1360,10 @@ dependencies = [ "checksum os_info 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ecb53e7b83e5016bf4ac041e15e02b0d240cb27072b19b651b0b4d8cd6bbda9" "checksum path-slash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a0858af4d9136275541f4eac7be1af70add84cf356d901799b065ac1b8ff6e2f" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +"checksum pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +"checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +"checksum pest_generator 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +"checksum pest_meta 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" "checksum pretty_env_logger 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" @@ -1264,6 +1394,7 @@ dependencies = [ "checksum serde_derive 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)" = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8" "checksum serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)" = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" "checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" +"checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" "checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" "checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" @@ -1277,6 +1408,7 @@ dependencies = [ "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum ucd-trie 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" "checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" diff --git a/Cargo.toml b/Cargo.toml index 8926c4a6..9e4d462c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,8 @@ sysinfo = "0.12.0" byte-unit = "3.0.3" starship_module_config_derive = { version = "0.1.0", path = "starship_module_config_derive" } yaml-rust = "0.4" +pest = "^2.1" +pest_derive = "^2.1" nom = "5.1.1" regex = "1.3.6" os_info = "2.0.2" diff --git a/src/config.rs b/src/config.rs index d3f868f1..23535155 100644 --- a/src/config.rs +++ b/src/config.rs @@ -305,7 +305,7 @@ impl Default for SegmentConfig<'static> { - 'italic' - '' (see the parse_color_string doc for valid color strings) */ -fn parse_style_string(style_string: &str) -> Option { +pub fn parse_style_string(style_string: &str) -> Option { style_string .split_whitespace() .fold(Some(ansi_term::Style::new()), |maybe_style, token| { diff --git a/src/formatter/mod.rs b/src/formatter/mod.rs new file mode 100644 index 00000000..b0607df0 --- /dev/null +++ b/src/formatter/mod.rs @@ -0,0 +1,5 @@ +pub mod model; +mod parser; +pub mod string_formatter; + +pub use string_formatter::StringFormatter; diff --git a/src/formatter/model.rs b/src/formatter/model.rs new file mode 100644 index 00000000..8e4bbea3 --- /dev/null +++ b/src/formatter/model.rs @@ -0,0 +1,17 @@ +use std::borrow::Cow; + +pub struct TextGroup<'a> { + pub format: Vec>, + pub style: Vec>, +} + +pub enum FormatElement<'a> { + Text(Cow<'a, str>), + Variable(Cow<'a, str>), + TextGroup(TextGroup<'a>), +} + +pub enum StyleElement<'a> { + Text(Cow<'a, str>), + Variable(Cow<'a, str>), +} diff --git a/src/formatter/parser.rs b/src/formatter/parser.rs new file mode 100644 index 00000000..95c267a3 --- /dev/null +++ b/src/formatter/parser.rs @@ -0,0 +1,76 @@ +use pest::{error::Error, iterators::Pair, Parser}; + +use super::model::*; + +#[derive(Parser)] +#[grammar = "formatter/spec.pest"] +struct IdentParser; + +fn _parse_textgroup(textgroup: Pair) -> TextGroup { + let mut inner_rules = textgroup.into_inner(); + let format = inner_rules.next().unwrap(); + let style = inner_rules.next().unwrap(); + + TextGroup { + format: _parse_format(format), + style: _parse_style(style), + } +} + +fn _parse_variable(variable: Pair) -> &str { + variable.into_inner().next().unwrap().as_str() +} + +fn _parse_text(text: Pair) -> String { + let mut result = String::new(); + for pair in text.into_inner() { + result.push_str(pair.as_str()); + } + result +} + +fn _parse_format(format: Pair) -> Vec { + let mut result: Vec = Vec::new(); + + for pair in format.into_inner() { + match pair.as_rule() { + Rule::text => result.push(FormatElement::Text(_parse_text(pair).into())), + Rule::variable => result.push(FormatElement::Variable(_parse_variable(pair).into())), + Rule::textgroup => result.push(FormatElement::TextGroup(_parse_textgroup(pair))), + _ => unreachable!(), + } + } + + result +} + +fn _parse_style(style: Pair) -> Vec { + let mut result: Vec = Vec::new(); + + for pair in style.into_inner() { + match pair.as_rule() { + Rule::text => result.push(StyleElement::Text(_parse_text(pair).into())), + Rule::variable => result.push(StyleElement::Variable(_parse_variable(pair).into())), + _ => unreachable!(), + } + } + + result +} + +pub fn parse(format: &str) -> Result, Error> { + let pairs = IdentParser::parse(Rule::expression, format)?; + let mut result: Vec = Vec::new(); + + // Lifetime of Segment is the same as result + for pair in pairs.take_while(|pair| pair.as_rule() != Rule::EOI) { + match pair.as_rule() { + Rule::text => result.push(FormatElement::Text(_parse_text(pair).into())), + Rule::variable => result.push(FormatElement::Variable(_parse_variable(pair).into())), + Rule::textgroup => result.push(FormatElement::TextGroup(_parse_textgroup(pair))), + _ => unreachable!(), + } + } + + Ok(result) +} diff --git a/src/formatter/spec.pest b/src/formatter/spec.pest new file mode 100644 index 00000000..36be53be --- /dev/null +++ b/src/formatter/spec.pest @@ -0,0 +1,16 @@ +expression = _{ SOI ~ value* ~ EOI } +value = _{ text | variable | textgroup } + +variable = { "$" ~ variable_name } +variable_name = @{ char+ } +char = _{ 'a'..'z' | 'A'..'Z' | '0'..'9' | "_" } + +text = { text_inner+ } +text_inner = _{ text_inner_char | escape } +text_inner_char = { !("[" | "]" | "(" | ")" | "$" | "\\") ~ ANY } +escape = _{ "\\" ~ escaped_char } +escaped_char = { "[" | "]" | "(" | ")" | "\\" | "$" } + +textgroup = { "[" ~ format ~ "]" ~ "(" ~ style ~ ")" } +format = { (variable | text | textgroup)* } +style = { (variable | text)* } diff --git a/src/formatter/string_formatter.rs b/src/formatter/string_formatter.rs new file mode 100644 index 00000000..70938973 --- /dev/null +++ b/src/formatter/string_formatter.rs @@ -0,0 +1,252 @@ +use ansi_term::Style; +use pest::error::Error; +use rayon::prelude::*; +use std::collections::BTreeMap; + +use crate::config::parse_style_string; +use crate::segment::Segment; + +use super::model::*; +use super::parser::{parse, Rule}; + +type VariableMapType = BTreeMap>>; + +pub struct StringFormatter<'a> { + format: Vec>, + variables: VariableMapType, +} + +impl<'a> StringFormatter<'a> { + /// Creates an instance of StringFormatter from a format string + pub fn new(format: &'a str) -> Result> { + parse(format) + .map(|format| { + let variables = _get_variables(&format); + (format, variables) + }) + .map(|(format, variables)| Self { format, variables }) + } + + /// Maps variable name to its value + pub fn map(mut self, mapper: impl Fn(&str) -> Option + Sync) -> Self { + self.variables.par_iter_mut().for_each(|(key, value)| { + *value = mapper(key).map(|value| vec![_new_segment(key.to_string(), value, None)]); + }); + self + } + + /// Maps variable name to an array of segments + pub fn map_variables_to_segments( + mut self, + mapper: impl Fn(&str) -> Option> + Sync, + ) -> Self { + self.variables.par_iter_mut().for_each(|(key, value)| { + *value = mapper(key); + }); + self + } + + /// Parse the format string and consume self. + pub fn parse(self, default_style: Option