From 7c6e46a23be93f1f47fa33cb2dcc99d75804f767 Mon Sep 17 00:00:00 2001 From: Robin van Boven <497556+Beanow@users.noreply.github.com> Date: Thu, 13 Jul 2023 16:38:00 +0200 Subject: [PATCH] Adding 2023 vote results & tools (#38) * feat: Adding a basic vote counter * docs: Include 2023 JSON votes * docs: Result summary in markdown --- board-election-2023/README.md | 24 +- board-election-2023/votes.ndjson | 15 + tools/.gitignore | 1 + tools/Cargo.lock | 455 +++++++++++++++++++++++++++++++ tools/Cargo.toml | 2 + tools/count-votes/Cargo.toml | 11 + tools/count-votes/src/main.rs | 132 +++++++++ 7 files changed, 637 insertions(+), 3 deletions(-) create mode 100644 board-election-2023/votes.ndjson create mode 100644 tools/.gitignore create mode 100644 tools/Cargo.lock create mode 100644 tools/Cargo.toml create mode 100644 tools/count-votes/Cargo.toml create mode 100644 tools/count-votes/src/main.rs diff --git a/board-election-2023/README.md b/board-election-2023/README.md index 668a0f0..8089f63 100644 --- a/board-election-2023/README.md +++ b/board-election-2023/README.md @@ -7,11 +7,29 @@ This year is the first election where we **invite Working Group members** to cas **Places you can reach out: [Discussion #31][discussion-31] | [`#general-wg`][discord-general-wg]** -## Result pending +## Election Result -_The vote is currently closed, after Wednesday July 12th, 2023_. +**All candidates met the theshold and were elected!** 🎉 -Results will be broadcasted and included here, on or before July 16, 2023. +We received **15 votes**. Meaning `>= 8` votes is the confidence threshold. + +| Name | Elected | Positive votes | Threshold | +| --------------- | :-----: | -------------: | --------: | +| David Lemarier | **Yes** | 15 | 8 | +| Jacob Bolda | **Yes** | 15 | 8 | +| Robin van Boven | **Yes** | 15 | 8 | +| Zack Chapple | **Yes** | 12 | 8 | +| Bill Avery | **Yes** | 10 | 8 | + +- _Raw votes: [`votes.ndjson`](./votes.ndjson)_ +- _Review: https://github.com/tauri-apps/governance-and-guidance/pull/38_ + +Taking as reference, currently 46 people (and 1 bot) have the Discord `working-group` role. +A ~33% turnout for the vote based on that number. + +The official end of term for the current Directors is _on July 16th_. After this and after making sure they've signed [the Plege][tcc-plege] the new Directors can take office. + +[tcc-plege]: https://dracc.commonsconservancy.org/0016/ ## Dates diff --git a/board-election-2023/votes.ndjson b/board-election-2023/votes.ndjson new file mode 100644 index 0000000..6d7e2d5 --- /dev/null +++ b/board-election-2023/votes.ndjson @@ -0,0 +1,15 @@ +{"yes":["bill-avery","zack-chapple","robin-van-boven","jacob-bolda","david-lemarier"],"no":[],"random_id":"10cd25b0-81ae-44de-a7ca-e80d966ba3db"} +{"yes":["robin-van-boven","jacob-bolda","david-lemarier","zack-chapple","bill-avery"],"no":[],"random_id":"1e2d544d-a32b-4edb-8665-2b77407085c2"} +{"yes":["bill-avery","david-lemarier","jacob-bolda","robin-van-boven"],"no":["zack-chapple"],"random_id":"3b4608a2-3eb1-43ec-b2af-faa2565333a0"} +{"yes":["zack-chapple","jacob-bolda","robin-van-boven","bill-avery","david-lemarier"],"no":[],"random_id":"59a569c0-518a-4b81-b844-8f8852c5742d"} +{"yes":["zack-chapple","jacob-bolda","david-lemarier","robin-van-boven"],"no":["bill-avery"],"random_id":"6af2d565-7034-48ec-915a-09be3eed3cd9"} +{"yes":["jacob-bolda","david-lemarier","robin-van-boven","bill-avery"],"no":["zack-chapple"],"random_id":"8ab8825e-1bf4-4b2e-bc87-a29743b63deb"} +{"yes":["zack-chapple","robin-van-boven","david-lemarier","jacob-bolda"],"no":["bill-avery"],"random_id":"93d419e0-b883-4818-a93e-4e13ef1f0dc6"} +{"yes":["robin-van-boven","jacob-bolda","bill-avery","david-lemarier","zack-chapple"],"no":[],"random_id":"b596651a-008f-4e7b-b6a8-395dbf7eea0d"} +{"yes":["robin-van-boven","david-lemarier","bill-avery","jacob-bolda","zack-chapple"],"no":[],"random_id":"ba280bc8-3f00-4898-9110-d7b8aefe881d"} +{"yes":["robin-van-boven","david-lemarier","zack-chapple","jacob-bolda"],"no":["bill-avery"],"random_id":"be357642-4fd7-46c2-a1c8-82abecae6616"} +{"yes":["robin-van-boven","jacob-bolda","david-lemarier"],"no":["bill-avery","zack-chapple"],"random_id":"bf189986-28ed-4343-8fc2-988e2372fb61"} +{"yes":["david-lemarier","robin-van-boven","jacob-bolda","bill-avery","zack-chapple"],"no":[],"random_id":"cab91417-6d7b-413f-9f62-56b483466579"} +{"yes":["robin-van-boven","david-lemarier","jacob-bolda","zack-chapple","bill-avery"],"no":[],"random_id":"cf2ad03e-54fc-4cfd-9579-84b312b5c9e0"} +{"yes":["robin-van-boven","jacob-bolda","david-lemarier","zack-chapple"],"no":["bill-avery"],"random_id":"eb6882a9-8697-4668-89d0-7c326346d57e"} +{"yes":["zack-chapple","jacob-bolda","david-lemarier","robin-van-boven","bill-avery"],"no":[],"random_id":"ff6f9de8-c043-4dd0-99f1-a7909a5b33c2"} diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 0000000..1de5659 --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1 @@ +target \ No newline at end of file diff --git a/tools/Cargo.lock b/tools/Cargo.lock new file mode 100644 index 0000000..d0b95f6 --- /dev/null +++ b/tools/Cargo.lock @@ -0,0 +1,455 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "count-votes" +version = "0.1.0" +dependencies = [ + "jsonl", + "serde", + "tokio", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "jsonl" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1abae98f45234fc1980c198798166a80ad6b35eb5b7db4caa7bc72ff919e6b80" +dependencies = [ + "serde", + "serde_json", + "thiserror", + "tokio", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "ryu" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg", + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/tools/Cargo.toml b/tools/Cargo.toml new file mode 100644 index 0000000..fbb9862 --- /dev/null +++ b/tools/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["count-votes"] diff --git a/tools/count-votes/Cargo.toml b/tools/count-votes/Cargo.toml new file mode 100644 index 0000000..3e460c8 --- /dev/null +++ b/tools/count-votes/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "count-votes" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +jsonl = { version = "4", features = ["tokio"] } +serde = { version = "1", features = ["derive"] } +tokio = { version = "1", features = ["full"] } diff --git a/tools/count-votes/src/main.rs b/tools/count-votes/src/main.rs new file mode 100644 index 0000000..340d98e --- /dev/null +++ b/tools/count-votes/src/main.rs @@ -0,0 +1,132 @@ +use serde::Deserialize; +use std::{ + collections::{HashMap, HashSet}, + fmt::Display, +}; +use tokio::io::{self, BufReader}; + +#[derive(Debug)] +enum Error { + /// A random_id was seen twice. + DuplicateVote, + + /// The invariant `yes + no == total` failed for a candidate. + /// It's a bit like double entry accounting. :] + InvariantYesNoSum(String), +} + +impl std::error::Error for Error {} + +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[derive(Deserialize, Debug)] +struct Vote { + yes: Vec, + no: Vec, + random_id: String, +} + +#[derive(Default, Debug)] +struct Count { + total: u32, + vote_ids: HashSet, + candidates: HashSet, + yes: HashMap, + no: HashMap, +} + +// We're printing this as Debug. +#[allow(dead_code)] +#[derive(Default, Debug)] +struct Summary { + votes: u32, + confidence_threshold: u32, + confidence: HashSet, + yes: HashMap, +} + +impl Count { + fn assert_unique_vote(&mut self, id: String) -> Result<(), Error> { + if !self.vote_ids.insert(id) { + Err(Error::DuplicateVote) + } else { + Ok(()) + } + } + + fn include_candidate(&mut self, candidate: &String) { + self.candidates.insert(candidate.to_string()); + } + + fn increment_yes(&mut self, candidate: &String) { + if let Some(c) = self.yes.get_mut(candidate) { + *c += 1; + } else { + self.yes.insert(candidate.to_string(), 1); + } + } + + fn increment_no(&mut self, candidate: &String) { + if let Some(c) = self.no.get_mut(candidate) { + *c += 1; + } else { + self.no.insert(candidate.to_string(), 1); + } + } +} + +impl TryFrom for Summary { + type Error = Error; + + fn try_from(value: Count) -> std::result::Result { + let confidence_threshold = (f64::from(value.total) / 2f64).ceil() as u32; + let mut confidence: HashSet = Default::default(); + + for candidate in value.candidates.iter() { + let yes = value.yes.get(candidate).map_or(0, ToOwned::to_owned); + let no = value.no.get(candidate).map_or(0, ToOwned::to_owned); + if yes + no != value.total { + return Err(Error::InvariantYesNoSum(candidate.to_string())); + } + + if yes >= confidence_threshold { + confidence.insert(candidate.to_string()); + } + } + + Ok(Self { + votes: value.total, + confidence_threshold, + confidence, + yes: value.yes, + }) + } +} + +#[tokio::main] +async fn main() -> Result<(), Error> { + let mut count = Count::default(); + let mut feed = BufReader::new(io::stdin()); + + while let Ok(vote) = jsonl::read::<_, Vote>(&mut feed).await { + count.total += 1; + for c in &vote.yes { + count.include_candidate(c); + count.increment_yes(c); + } + for c in &vote.no { + count.include_candidate(c); + count.increment_no(c); + } + count.assert_unique_vote(vote.random_id)? + } + + let summary: Summary = count.try_into()?; + println!("{:#?}", summary); + + Ok(()) +}