├── .gitignore ├── images └── spacer.gif ├── Cargo.toml ├── LICENSE ├── README.md ├── .github └── workflows │ └── build.yml ├── src └── main.rs └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea/ 3 | -------------------------------------------------------------------------------- /images/spacer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samwho/spacer/HEAD/images/spacer.gif -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spacer" 3 | version = "0.4.5" 4 | edition = "2024" 5 | license = "MIT" 6 | authors = ["Sam Rose "] 7 | keywords = ["cli"] 8 | categories = ["command-line-utilities"] 9 | repository = "https://github.com/samwho/spacer" 10 | description = "A CLI utility for adding spacers when command output stops" 11 | include = ["Cargo.toml", "LICENSE", "README.md", "**/*.rs"] 12 | 13 | [badges] 14 | maintenance = { status = "passively-maintained" } 15 | 16 | [profile.release] 17 | opt-level = 3 18 | lto = true 19 | codegen-units = 1 20 | 21 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 22 | 23 | [dependencies] 24 | clap = { version = "4.3", features = ["derive"] } 25 | anyhow = "1.0" 26 | terminal_size = "0.4" 27 | lazy_static = "1.5" 28 | owo-colors = { version = "4", features = ["supports-colors"] } 29 | log = "0.4" 30 | env_logger = "0.11" 31 | human-panic = "2.0" 32 | chrono = "0.4" 33 | chrono-tz = "0.10" 34 | 35 | [dev-dependencies] 36 | test-case = "3" 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Sam Rose 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spacer 2 | 3 | [![Build status](https://github.com/samwho/spacer/workflows/Build/badge.svg)](https://github.com/samwho/spacer/actions) 4 | [![Crates.io](https://img.shields.io/crates/v/spacer.svg)](https://crates.io/crates/spacer) 5 | 6 | `spacer` is a simple CLI tool to insert spacers when command output stops. 7 | 8 | If you're the type of person that habitually presses enter a few times in your 9 | log tail to know where the last request ended and the new one begins, this tool 10 | is for you! 11 | 12 | ![](/images/spacer.gif) 13 | 14 | ## Installation 15 | 16 | With Homebrew: 17 | 18 | ```bash 19 | brew install spacer 20 | ``` 21 | 22 | Direct from Cargo: 23 | 24 | ```bash 25 | cargo install spacer 26 | ``` 27 | 28 | ## Usage 29 | 30 | By default, `spacer` outputs a spacer after 1 second with no output. You can 31 | change this with the `--after` flag. 32 | 33 | ```bash 34 | tail -f some.log | spacer --after 5 35 | ``` 36 | 37 | `--after` accepts a number of seconds, and allows floating point numbers for 38 | extra precision. 39 | 40 | ## STDOUT and STDERR 41 | 42 | Some commands output most of their information on STDERR, not STDOUT. `spacer` 43 | only monitors STDOUT, so if you find a situation where `spacer` doesn't seem 44 | to be working it could be that the program you're piping from is using STDERR. 45 | 46 | To "fix" that, pipe both STDERR to STDOUT to spacer by using `|&` instead of 47 | `|` as the pipe characters: 48 | 49 | ```bash 50 | my-command |& spacer 51 | ``` 52 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - "docs/**" 7 | - "**.md" 8 | - "LICENSE" 9 | 10 | jobs: 11 | # Ensure that the project could be successfully compiled 12 | cargo_check: 13 | name: Check Code Compiles 14 | runs-on: ubuntu-22.04 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: actions-rust-lang/setup-rust-toolchain@v1 18 | with: 19 | toolchain: stable 20 | override: true 21 | - run: cargo check --all 22 | 23 | # Run the `rustfmt` code formatter 24 | rustfmt: 25 | name: Rustfmt [Formatter] 26 | needs: cargo_check 27 | runs-on: ubuntu-22.04 28 | steps: 29 | - uses: actions/checkout@v4 30 | - uses: actions-rust-lang/setup-rust-toolchain@v1 31 | with: 32 | toolchain: stable 33 | components: rustfmt 34 | override: true 35 | - run: cargo fmt --all -- --check 36 | 37 | # Run the `clippy` linting tool 38 | clippy: 39 | name: Clippy [Linter] 40 | needs: cargo_check 41 | runs-on: ubuntu-22.04 42 | permissions: 43 | checks: write 44 | steps: 45 | - uses: actions/checkout@v4 46 | - uses: actions-rust-lang/setup-rust-toolchain@v1 47 | with: 48 | toolchain: stable 49 | components: clippy 50 | override: true 51 | - run: cargo clippy --all-targets --all-features -- -D clippy::all 52 | 53 | cargo_audit: 54 | name: Cargo Audit [Security] 55 | runs-on: ubuntu-22.04 56 | steps: 57 | - uses: actions/checkout@v4 58 | - uses: actions-rust-lang/audit@v1 59 | 60 | test: 61 | needs: [cargo_check, rustfmt, clippy] 62 | name: Tests 63 | runs-on: ${{ matrix.os }} 64 | strategy: 65 | matrix: 66 | os: [ubuntu-22.04, windows-latest, macOS-latest] 67 | rust: [stable, nightly] 68 | 69 | steps: 70 | - uses: actions/checkout@v4 71 | - uses: actions-rust-lang/setup-rust-toolchain@v1 72 | with: 73 | toolchain: ${{ matrix.rust }} 74 | override: true 75 | - run: cargo test 76 | 77 | github_build: 78 | if: startsWith(github.ref, 'refs/tags/v') 79 | name: Build release binaries 80 | strategy: 81 | matrix: 82 | target: 83 | - x86_64-unknown-linux-gnu 84 | - x86_64-unknown-linux-musl 85 | - x86_64-apple-darwin 86 | - x86_64-pc-windows-msvc 87 | - aarch64-unknown-linux-gnu 88 | - aarch64-unknown-linux-musl 89 | - aarch64-apple-darwin 90 | - aarch64-pc-windows-msvc 91 | include: 92 | - target: x86_64-unknown-linux-gnu 93 | os: ubuntu-22.04 94 | name: spacer-x86_64-unknown-linux-gnu.tar.gz 95 | - target: x86_64-unknown-linux-musl 96 | os: ubuntu-22.04 97 | name: spacer-x86_64-unknown-linux-musl.tar.gz 98 | - target: x86_64-apple-darwin 99 | os: macOS-latest 100 | name: spacer-x86_64-apple-darwin.tar.gz 101 | - target: x86_64-pc-windows-msvc 102 | os: windows-latest 103 | name: spacer-x86_64-pc-windows-msvc.zip 104 | - target: aarch64-unknown-linux-gnu 105 | os: ubuntu-22.04 106 | name: spacer-aarch64-unknown-linux-gnu.tar.gz 107 | - target: aarch64-unknown-linux-musl 108 | os: ubuntu-22.04 109 | name: spacer-aarch64-unknown-linux-musl.tar.gz 110 | - target: aarch64-apple-darwin 111 | os: macOS-latest 112 | name: spacer-aarch64-apple-darwin.tar.gz 113 | - target: aarch64-pc-windows-msvc 114 | os: windows-latest 115 | name: spacer-aarch64-pc-windows-msvc.zip 116 | runs-on: ${{ matrix.os }} 117 | steps: 118 | - uses: actions/checkout@v4 119 | 120 | - name: Build binary 121 | uses: houseabsolute/actions-rust-cross@v1 122 | with: 123 | command: build 124 | target: ${{ matrix.target }} 125 | args: "--locked --release" 126 | strip: true 127 | 128 | - name: Prepare build artifacts [Windows] 129 | if: matrix.os == 'windows-latest' 130 | run: | 131 | cd target/${{ matrix.target }}/release 132 | 7z a ../../../${{ matrix.name }} spacer.exe 133 | cd - 134 | 135 | - name: Prepare build artifacts [-nix] 136 | if: matrix.os != 'windows-latest' 137 | run: | 138 | cd target/${{ matrix.target }}/release 139 | tar czvf ../../../${{ matrix.name }} spacer 140 | cd - 141 | 142 | - name: Upload build artifact 143 | uses: actions/upload-artifact@v4 144 | with: 145 | name: ${{ matrix.name }} 146 | path: ${{ matrix.name }} 147 | 148 | github_release: 149 | if: startsWith(github.ref, 'refs/tags/v') 150 | name: Create GitHub Release 151 | needs: [test, github_build, cargo_audit] 152 | runs-on: ubuntu-22.04 153 | steps: 154 | - uses: actions/checkout@v4 155 | 156 | - name: Download releases from github_build 157 | uses: actions/download-artifact@v4 158 | with: 159 | name: spacer-x86_64-unknown-linux-gnu.tar.gz 160 | path: . 161 | - name: Download releases from github_build 162 | uses: actions/download-artifact@v4 163 | with: 164 | name: spacer-x86_64-unknown-linux-musl.tar.gz 165 | path: . 166 | - name: Download releases from github_build 167 | uses: actions/download-artifact@v4 168 | with: 169 | name: spacer-x86_64-apple-darwin.tar.gz 170 | path: . 171 | - name: Download releases from github_build 172 | uses: actions/download-artifact@v4 173 | with: 174 | name: spacer-x86_64-pc-windows-msvc.zip 175 | path: . 176 | - name: Download releases from github_build 177 | uses: actions/download-artifact@v4 178 | with: 179 | name: spacer-aarch64-unknown-linux-gnu.tar.gz 180 | path: . 181 | - name: Download releases from github_build 182 | uses: actions/download-artifact@v4 183 | with: 184 | name: spacer-aarch64-unknown-linux-musl.tar.gz 185 | path: . 186 | - name: Download releases from github_build 187 | uses: actions/download-artifact@v4 188 | with: 189 | name: spacer-aarch64-apple-darwin.tar.gz 190 | path: . 191 | - name: Download releases from github_build 192 | uses: actions/download-artifact@v4 193 | with: 194 | name: spacer-aarch64-pc-windows-msvc.zip 195 | path: . 196 | 197 | - name: Generate checksums 198 | run: for file in spacer-*; do openssl dgst -sha256 -r "$file" > "${file}.sha256"; done 199 | 200 | - name: Create GitHub release 201 | uses: softprops/action-gh-release@v1 202 | with: 203 | files: | 204 | spacer-x86_64-unknown-linux-gnu.tar.gz 205 | spacer-x86_64-unknown-linux-gnu.tar.gz.sha256 206 | spacer-x86_64-unknown-linux-musl.tar.gz 207 | spacer-x86_64-unknown-linux-musl.tar.gz.sha256 208 | spacer-x86_64-apple-darwin.tar.gz 209 | spacer-x86_64-apple-darwin.tar.gz.sha256 210 | spacer-x86_64-pc-windows-msvc.zip 211 | spacer-x86_64-pc-windows-msvc.zip.sha256 212 | spacer-aarch64-unknown-linux-gnu.tar.gz 213 | spacer-aarch64-unknown-linux-gnu.tar.gz.sha256 214 | spacer-aarch64-unknown-linux-musl.tar.gz 215 | spacer-aarch64-unknown-linux-musl.tar.gz.sha256 216 | spacer-aarch64-apple-darwin.tar.gz 217 | spacer-aarch64-apple-darwin.tar.gz.sha256 218 | spacer-aarch64-pc-windows-msvc.zip 219 | spacer-aarch64-pc-windows-msvc.zip.sha256 220 | env: 221 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 222 | 223 | cargo_publish: 224 | needs: [test, cargo_audit] 225 | if: startsWith(github.ref, 'refs/tags/v') 226 | name: Publish Cargo Package 227 | runs-on: ubuntu-22.04 228 | steps: 229 | - uses: actions/checkout@v4 230 | - uses: actions-rust-lang/setup-rust-toolchain@v1 231 | with: 232 | toolchain: stable 233 | override: true 234 | - run: cargo login $CRATES_IO_TOKEN 235 | - run: cargo publish 236 | env: 237 | CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }} 238 | 239 | aur-publish-spacer: 240 | needs: [github_release] 241 | runs-on: ubuntu-22.04 242 | if: startsWith(github.ref, 'refs/tags/v') 243 | steps: 244 | - uses: actions/checkout@v4 245 | 246 | - name: Get tag version 247 | id: get_tag 248 | run: echo "tag=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT 249 | 250 | - name: Download source tarball 251 | run: | 252 | curl -L -o spacer-${{ steps.get_tag.outputs.tag }}.tar.gz https://github.com/samwho/spacer/archive/v${{ steps.get_tag.outputs.tag }}.tar.gz 253 | 254 | - name: Calculate SHA-256 checksum 255 | id: calculate_checksum 256 | run: | 257 | sha256sum spacer-${{ steps.get_tag.outputs.tag }}.tar.gz | awk '{ print $1 }' > checksum.txt 258 | echo "sha256=$(cat checksum.txt)" >> $GITHUB_OUTPUT 259 | cat checksum.txt 260 | 261 | - name: Generate PKGBUILD 262 | run: | 263 | cat < PKGBUILD 264 | # Maintainer: Sam Rose 265 | 266 | pkgname='spacer' 267 | pkgdesc='CLI tool to insert spacers in when command output stops' 268 | pkgver='${{ steps.get_tag.outputs.tag }}' 269 | pkgrel='1' 270 | arch=('x86_64' 'aarch64') 271 | url='https://github.com/samwho/spacer' 272 | license=('MIT') 273 | depends=('gcc-libs') 274 | optdepends=() 275 | makedepends=('rust' 'gcc' 'binutils') 276 | checkdepends=('rust' 'git') 277 | provides=(spacer) 278 | source=("\$pkgname-\$pkgver.tar.gz::https://github.com/samwho/spacer/archive/v\${pkgver}.tar.gz") 279 | sha256sums=('${{ steps.calculate_checksum.outputs.sha256 }}') 280 | 281 | build() { 282 | cd "\$pkgname-\$pkgver" 283 | /usr/bin/cargo build --release 284 | } 285 | 286 | check() { 287 | cd "\$pkgname-\$pkgver" 288 | /usr/bin/cargo test 289 | } 290 | 291 | package() { 292 | cd "\$pkgname-\$pkgver" 293 | strip target/release/spacer 294 | install -Dm755 "target/release/spacer" "\$pkgdir/usr/bin/spacer" 295 | install -Dm644 "LICENSE" "\$pkgdir/usr/share/licenses/\$pkgname/LICENSE" 296 | } 297 | EOF 298 | 299 | cat PKGBUILD 300 | 301 | - name: Publish AUR package 302 | uses: KSXGitHub/github-actions-deploy-aur@v4.1.1 303 | with: 304 | pkgname: spacer 305 | pkgbuild: ./PKGBUILD 306 | commit_username: ${{ secrets.AUR_USERNAME }} 307 | commit_email: ${{ secrets.AUR_EMAIL }} 308 | ssh_private_key: ${{ secrets.AUR_SECRET_KEY }} 309 | commit_message: Update AUR package 310 | ssh_keyscan_types: rsa,ecdsa,ed25519 311 | test: true 312 | test_flags: "--cleanbuild --syncdeps --noconfirm" 313 | 314 | aur-publish-spacer-bin: 315 | needs: [github_release] 316 | runs-on: ubuntu-22.04 317 | if: startsWith(github.ref, 'refs/tags/v') 318 | steps: 319 | - uses: actions/checkout@v4 320 | 321 | - name: Get tag version 322 | id: get_tag 323 | run: echo "tag=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT 324 | 325 | - name: Download source tarball 326 | run: | 327 | curl -L -o spacer-${{ steps.get_tag.outputs.tag }}.tar.gz https://github.com/samwho/spacer/releases/download/v${{ steps.get_tag.outputs.tag }}/spacer-x86_64-unknown-linux-gnu.tar.gz 328 | 329 | - name: Calculate SHA-256 checksum 330 | id: calculate_checksum 331 | run: | 332 | sha256sum spacer-${{ steps.get_tag.outputs.tag }}.tar.gz | awk '{ print $1 }' > checksum.txt 333 | echo "sha256=$(cat checksum.txt)" >> $GITHUB_OUTPUT 334 | cat checksum.txt 335 | 336 | - name: Generate PKGBUILD 337 | run: | 338 | cat < PKGBUILD 339 | # Maintainer: Sam Rose 340 | 341 | pkgname='spacer-bin' 342 | pkgdesc='CLI tool to insert spacers in when command output stops' 343 | pkgver='${{ steps.get_tag.outputs.tag }}' 344 | pkgrel='1' 345 | arch=('x86_64') 346 | url='https://github.com/samwho/spacer' 347 | license=('MIT') 348 | depends=() 349 | optdepends=() 350 | makedepends=() 351 | checkdepends=() 352 | provides=(spacer) 353 | conflicts=(spacer) 354 | source=("\${pkgname}-v\${pkgver}.tar.gz::https://github.com/samwho/spacer/releases/download/v\${pkgver}/spacer-x86_64-unknown-linux-gnu.tar.gz") 355 | sha256sums=('${{ steps.calculate_checksum.outputs.sha256 }}') 356 | 357 | package() { 358 | install -Dm755 "spacer" "\$pkgdir/usr/bin/spacer" 359 | } 360 | EOF 361 | 362 | cat PKGBUILD 363 | 364 | - name: Publish AUR package 365 | uses: KSXGitHub/github-actions-deploy-aur@v4.1.1 366 | with: 367 | pkgname: spacer-bin 368 | pkgbuild: ./PKGBUILD 369 | commit_username: ${{ secrets.AUR_USERNAME }} 370 | commit_email: ${{ secrets.AUR_EMAIL }} 371 | ssh_private_key: ${{ secrets.AUR_SECRET_KEY }} 372 | commit_message: Update AUR package 373 | ssh_keyscan_types: rsa,ecdsa,ed25519 374 | test: true 375 | test_flags: "--cleanbuild --syncdeps --noconfirm" 376 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | 3 | use chrono::{DateTime, Local}; 4 | use chrono_tz::Tz; 5 | use clap::Parser; 6 | use human_panic::setup_panic; 7 | use log::debug; 8 | use owo_colors::{self, OwoColorize, Stream}; 9 | use std::time::Instant; 10 | use std::{ 11 | io::{BufRead, Write, stdin, stdout}, 12 | sync::{ 13 | Arc, Mutex, 14 | atomic::{AtomicBool, Ordering}, 15 | }, 16 | thread::{scope, sleep, spawn}, 17 | }; 18 | use terminal_size::{Height, Width}; 19 | 20 | #[derive(Parser, Clone, Debug)] 21 | #[command(author, version, about, long_about = None)] 22 | struct Args { 23 | /// Minimum number of seconds that have to pass before a spacer is printed 24 | #[arg(long, short, default_value = "1.0")] 25 | after: f64, 26 | 27 | /// Which character to use as a spacer 28 | #[arg(long, short, default_value = "━")] 29 | dash: char, 30 | 31 | /// Number of newlines to print before and after spacer lines 32 | #[arg(long, short, default_value = "0")] 33 | padding: usize, 34 | 35 | /// Number of characters to print on a spacer line 36 | #[arg(long, short)] 37 | width: Option, 38 | 39 | /// Enable stopwatch mode, which will increment a counter in real-time on 40 | /// the spacer 41 | #[arg(long)] 42 | stopwatch: bool, 43 | 44 | /// Force the output to not be colorized 45 | #[arg(long, group = "color-overrides", default_value = "false")] 46 | no_color: bool, 47 | 48 | /// Force the output to be colorized, even if the output is not a TTY 49 | #[arg(long, group = "color-overrides", default_value = "false")] 50 | force_color: bool, 51 | 52 | /// Put the timestamp on the right side of the spacer. 53 | #[arg(long, default_value = "false")] 54 | right: bool, 55 | 56 | /// Print timestamp in an arbitrary timezone (in IANA format, e.g. Europe/London). 57 | #[arg(long)] 58 | timezone: Option, 59 | } 60 | 61 | struct TestStats { 62 | wakeups: usize, 63 | } 64 | 65 | impl TestStats { 66 | // This is only used in tests. 67 | #[allow(dead_code)] 68 | fn new() -> Self { 69 | Self { wakeups: 0 } 70 | } 71 | } 72 | 73 | fn format_elapsed(seconds: f64) -> String { 74 | let minutes = seconds / 60.0; 75 | let hours = minutes / 60.0; 76 | let days = hours / 24.0; 77 | let weeks = days / 7.0; 78 | let months = days / 30.0; 79 | let years = days / 365.0; 80 | 81 | if years >= 1.0 { 82 | format!("{years:.1}y") 83 | } else if months >= 1.0 { 84 | format!("{months:.1}mo") 85 | } else if weeks >= 1.0 { 86 | format!("{weeks:.1}w") 87 | } else if days >= 1.0 { 88 | format!("{days:.1}d") 89 | } else if hours >= 1.0 { 90 | format!("{hours:.1}h") 91 | } else if minutes >= 1.0 { 92 | format!("{minutes:.1}m") 93 | } else { 94 | format!("{seconds:.1}s") 95 | } 96 | } 97 | 98 | fn print_spacer( 99 | output: Arc>, 100 | args: &Args, 101 | last_spacer: &Instant, 102 | stop_flag: Option>, 103 | ) -> Result<()> { 104 | let (width, _) = terminal_size::terminal_size().unwrap_or((Width(80), Height(24))); 105 | debug!("terminal width: {:?}", width); 106 | 107 | let mut written: u16 = 0; 108 | 109 | let datetime_strings = match args.timezone.clone() { 110 | None => { 111 | let now: DateTime = Local::now(); 112 | ( 113 | now.format("%Y-%m-%d").to_string(), 114 | now.format("%H:%M:%S").to_string(), 115 | ) 116 | } 117 | Some(timezone_str) => match timezone_str.parse::() { 118 | Ok(timezone) => { 119 | let now: DateTime = Local::now().with_timezone(&timezone); 120 | ( 121 | now.format("%Y-%m-%d").to_string(), 122 | now.format("%H:%M:%S %Z").to_string(), 123 | ) 124 | } 125 | Err(err) => { 126 | eprintln!("Error: {err}"); 127 | debug!("could not parse supplied timezone name, using local time"); 128 | let now: DateTime = Local::now(); 129 | ( 130 | now.format("%Y-%m-%d").to_string(), 131 | now.format("%H:%M:%S").to_string(), 132 | ) 133 | } 134 | }, 135 | }; 136 | 137 | let date_str = datetime_strings.0; 138 | let mut buf = Vec::new(); 139 | write!( 140 | buf, 141 | "{} ", 142 | date_str.if_supports_color(Stream::Stdout, |t| t.green()) 143 | )?; 144 | 145 | written += (date_str.len() + 1) as u16; 146 | 147 | let time_str = datetime_strings.1; 148 | write!( 149 | buf, 150 | "{} ", 151 | time_str.if_supports_color(Stream::Stdout, |t| t.yellow()) 152 | )?; 153 | written += (time_str.len() + 1) as u16; 154 | 155 | let elapsed_seconds = last_spacer.elapsed().as_secs_f64(); 156 | if elapsed_seconds > 0.1 { 157 | let elapsed = format_elapsed(elapsed_seconds); 158 | write!( 159 | buf, 160 | "{} ", 161 | elapsed.if_supports_color(Stream::Stdout, |t| t.blue()) 162 | )?; 163 | written += (elapsed.len() + 1) as u16; 164 | } 165 | 166 | let spacer_right = args.right; 167 | let padding = args.padding; 168 | let dash = args.dash; 169 | let width = args.width; 170 | let stopwatch = args.stopwatch; 171 | 172 | spawn(move || -> Result<()> { 173 | let start_waiting = Instant::now(); 174 | let mut output = output.lock().expect("failed to lock output"); 175 | 176 | if padding > 0 { 177 | writeln!(output, "{}", "\n".repeat(padding - 1))?; 178 | } 179 | 180 | loop { 181 | let mut buf = buf.clone(); 182 | 183 | let written = if stopwatch { 184 | let elapsed_time = start_waiting.elapsed().as_secs_f64(); 185 | let time_waiting = format_elapsed(elapsed_time); 186 | 187 | write!( 188 | buf, 189 | "{} ", 190 | time_waiting.if_supports_color(Stream::Stdout, |t| t.purple()) 191 | )?; 192 | written + (time_waiting.len() + 1) as u16 193 | } else { 194 | written 195 | }; 196 | 197 | let dashes = width.unwrap_or_else(|| { 198 | terminal_size::terminal_size() 199 | .map(|(Width(w), _)| w) 200 | .unwrap_or(80) 201 | .saturating_sub(written) 202 | }); 203 | 204 | if spacer_right { 205 | buf.pop(); 206 | let info = String::from_utf8(buf.clone())?; 207 | let mut spacer = Vec::new(); 208 | write!( 209 | spacer, 210 | "{} {}", 211 | dash.to_string() 212 | .repeat(dashes as usize) 213 | .as_str() 214 | .if_supports_color(Stream::Stdout, |t| t.dimmed()), 215 | info 216 | )?; 217 | write!(output, "\r{}", String::from_utf8(spacer)?)? 218 | } else { 219 | write!( 220 | buf, 221 | "{}", 222 | dash.to_string() 223 | .repeat(dashes as usize) 224 | .as_str() 225 | .if_supports_color(Stream::Stdout, |t| t.dimmed()) 226 | )?; 227 | write!(output, "\r{}", String::from_utf8(buf)?)?; 228 | } 229 | 230 | if !stopwatch || stop_flag.as_ref().unwrap().load(Ordering::Relaxed) { 231 | writeln!(output)?; 232 | if padding > 0 { 233 | writeln!(output, "{}", "\n".repeat(padding - 1))?; 234 | } 235 | 236 | break; 237 | } else { 238 | sleep(std::time::Duration::from_millis(10)) 239 | } 240 | } 241 | 242 | Ok(()) 243 | }); 244 | 245 | Ok(()) 246 | } 247 | 248 | fn run( 249 | input: impl BufRead, 250 | output: impl Write + Send + 'static, 251 | args: Args, 252 | mut test_stats: Option<&mut TestStats>, 253 | ) -> Result<()> { 254 | if args.no_color { 255 | owo_colors::set_override(false); 256 | } 257 | 258 | if args.force_color { 259 | owo_colors::set_override(true); 260 | } 261 | 262 | let last_line = Mutex::new(Instant::now()); 263 | let mut last_spacer = Instant::now(); 264 | let output = Arc::new(Mutex::new(output)); 265 | let finished = Mutex::new(false); 266 | let args = args.clone(); 267 | let stop_flag = Arc::new(AtomicBool::new(false)); 268 | 269 | scope(|s| { 270 | s.spawn(|| { 271 | loop { 272 | if let Some(test_stats) = &mut test_stats { 273 | test_stats.wakeups += 1; 274 | } 275 | 276 | if *finished.lock().unwrap() { 277 | debug!("thread received finish signal, exiting"); 278 | break; 279 | } 280 | 281 | debug!("begin loop"); 282 | 283 | let last_line = last_line.lock().unwrap(); 284 | if last_spacer >= *last_line { 285 | drop(last_line); 286 | 287 | debug!("last spacer is newer than last line, sleeping"); 288 | 289 | // We sleep here because we know that we're going to sleep for 290 | // a bare minimum of the --after interval. 291 | sleep(std::time::Duration::from_millis( 292 | (args.after * 1000.0) as u64, 293 | )); 294 | continue; 295 | } 296 | 297 | let elapsed_since_line = last_line.elapsed().as_secs_f64(); 298 | drop(last_line); 299 | 300 | if elapsed_since_line >= args.after { 301 | debug!("last line is older than --after, printing spacer"); 302 | 303 | let stop_flag = if args.stopwatch { 304 | stop_flag.store(false, Ordering::Relaxed); 305 | Some(Arc::clone(&stop_flag)) 306 | } else { 307 | None 308 | }; 309 | let output = Arc::clone(&output); 310 | print_spacer(output, &args, &last_spacer, stop_flag).unwrap(); 311 | 312 | last_spacer.clone_from(&Instant::now()); 313 | 314 | // We sleep here because we know that we're going to sleep for 315 | // a bare minimum of the --after interval. 316 | sleep(std::time::Duration::from_millis( 317 | (args.after * 1000.0) as u64, 318 | )); 319 | } else { 320 | // When calculating how long to sleep for, we want to make sure 321 | // that we sleep for at least 10ms, so that we don't spin too 322 | // much. 323 | let sleep_for = f64::max(0.01, args.after - elapsed_since_line); 324 | debug!( 325 | "last line is newer than --after, sleeping for {:.2}s", 326 | sleep_for 327 | ); 328 | 329 | // We sleep for as long as it takes to get to the number of 330 | // seconds --after the last line was printed. 331 | sleep(std::time::Duration::from_millis( 332 | (sleep_for * 1000.0) as u64, 333 | )); 334 | } 335 | } 336 | }); 337 | 338 | for line in input.lines() { 339 | let line = line.context("Failed to read line")?; 340 | let mut last_line = last_line.lock().unwrap(); 341 | last_line.clone_from(&Instant::now()); 342 | drop(last_line); 343 | if args.stopwatch { 344 | stop_flag.store(true, Ordering::Relaxed); 345 | } 346 | let mut out = output.lock().unwrap(); 347 | writeln!(out, "{line}")?; 348 | } 349 | 350 | debug!("signalling thread to finish"); 351 | let mut finished = finished.lock().unwrap(); 352 | *finished = true; 353 | 354 | Ok(()) 355 | }) 356 | } 357 | 358 | fn main() -> Result<()> { 359 | setup_panic!(); 360 | env_logger::init(); 361 | 362 | let args = Args::parse(); 363 | debug!("args: {:?}", args); 364 | 365 | run(stdin().lock(), stdout(), args, None) 366 | } 367 | 368 | #[cfg(test)] 369 | mod tests { 370 | use self::Op::*; 371 | use self::Out::*; 372 | use super::*; 373 | use std::io::{BufReader, Read}; 374 | use std::thread::sleep; 375 | use std::time::Duration; 376 | use test_case::test_case; 377 | 378 | enum Op { 379 | Sleep(u64), 380 | Write(&'static str), 381 | WriteLn(&'static str), 382 | } 383 | 384 | enum Out { 385 | Line(&'static str), 386 | Spacer, 387 | RightSpacer, 388 | SpacerWithLondonTimezone, 389 | RightSpacerWithLondonTimezone, 390 | CustomWidthSpacer(usize), 391 | } 392 | 393 | struct TimedInput { 394 | ops: Vec, 395 | index: usize, 396 | } 397 | 398 | impl TimedInput { 399 | fn new(ops: Vec) -> Self { 400 | Self { ops, index: 0 } 401 | } 402 | } 403 | 404 | impl Read for TimedInput { 405 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result { 406 | loop { 407 | if self.index >= self.ops.len() { 408 | return Ok(0); 409 | } 410 | 411 | let op = &self.ops[self.index]; 412 | self.index += 1; 413 | match op { 414 | Op::Sleep(duration) => { 415 | sleep(Duration::from_millis(*duration)); 416 | } 417 | Op::Write(string) => { 418 | let bytes = string.as_bytes(); 419 | buf[..bytes.len()].clone_from_slice(bytes); 420 | return Ok(bytes.len()); 421 | } 422 | Op::WriteLn(string) => { 423 | let str = format!("{string}\n"); 424 | let bytes = str.as_bytes(); 425 | buf[..bytes.len()].clone_from_slice(bytes); 426 | return Ok(bytes.len()); 427 | } 428 | } 429 | } 430 | } 431 | } 432 | 433 | struct SharedBuffer { 434 | buffer: Arc>>, 435 | } 436 | 437 | impl SharedBuffer { 438 | fn new() -> (Self, Arc>>) { 439 | let buffer = Arc::new(Mutex::new(Vec::new())); 440 | let shared = Self { 441 | buffer: Arc::clone(&buffer), 442 | }; 443 | (shared, buffer) 444 | } 445 | } 446 | 447 | impl std::io::Write for SharedBuffer { 448 | fn write(&mut self, buf: &[u8]) -> std::io::Result { 449 | self.buffer.lock().unwrap().write(buf) 450 | } 451 | 452 | fn flush(&mut self) -> std::io::Result<()> { 453 | self.buffer.lock().unwrap().flush() 454 | } 455 | } 456 | 457 | fn test_args() -> Args { 458 | Args { 459 | after: 0.1, 460 | dash: '-', 461 | padding: 0, 462 | width: None, 463 | stopwatch: true, 464 | no_color: true, 465 | force_color: false, 466 | right: false, 467 | timezone: None, 468 | } 469 | } 470 | 471 | #[test_case(vec![], vec![], test_args() ; "no output")] 472 | #[test_case(vec![Sleep(300)], vec![], test_args() ; "no output, after sleep")] 473 | #[test_case( 474 | vec![WriteLn("foo"), Sleep(300)], 475 | vec![Line("foo"), Spacer], 476 | test_args() 477 | ; "single line" 478 | )] 479 | #[test_case( 480 | vec![WriteLn("foo"), Sleep(300), WriteLn("bar"), WriteLn("baz"), Sleep(300)], 481 | vec![Line("foo"), Spacer, Line("bar"), Line("baz"), Spacer], 482 | test_args() 483 | ; "multiple lines" 484 | )] 485 | #[test_case( 486 | vec![WriteLn("foo"), WriteLn("bar"), WriteLn("baz")], 487 | vec![Line("foo"), Line("bar"), Line("baz")], 488 | test_args() 489 | ; "multiple lines, no sleeps" 490 | )] 491 | #[test_case( 492 | vec![Write("foo"), Write("bar"), Sleep(300), WriteLn("baz")], 493 | vec![Line("foobarbaz")], 494 | test_args() 495 | ; "single line, sleep in the middle" 496 | )] 497 | #[test_case( 498 | vec![WriteLn("foo"), Sleep(300)], 499 | vec![Line("foo"), RightSpacer], 500 | Args { 501 | after: 0.1, 502 | dash: '-', 503 | padding: 0, 504 | width: None, 505 | stopwatch: true, 506 | no_color: true, 507 | force_color: false, 508 | right: true, 509 | timezone: None, 510 | } 511 | ; "single line, right spacer" 512 | )] 513 | #[test_case( 514 | vec![WriteLn("foo"), Sleep(300)], 515 | vec![Line("foo"), Line(""), Spacer], 516 | Args { 517 | after: 0.1, 518 | dash: '-', 519 | padding: 1, 520 | width: None, 521 | stopwatch: true, 522 | no_color: true, 523 | force_color: false, 524 | right: false, 525 | timezone: None, 526 | } 527 | ; "padding = 1" 528 | )] 529 | #[test_case( 530 | vec![WriteLn("foo"), Sleep(300)], 531 | vec![Line("foo"), Line(""), Line(""), Spacer, Line(""), Line("")], 532 | Args { 533 | after: 0.1, 534 | dash: '-', 535 | padding: 2, 536 | width: None, 537 | stopwatch: false, 538 | no_color: true, 539 | force_color: false, 540 | right: false, 541 | timezone: None, 542 | } 543 | ; "padding = 2" 544 | )] 545 | #[test_case( 546 | vec![WriteLn("foo"), Sleep(300)], 547 | vec![Line("foo"), SpacerWithLondonTimezone], 548 | Args { 549 | after: 0.1, 550 | dash: '-', 551 | padding: 0, 552 | width: None, 553 | stopwatch: true, 554 | no_color: true, 555 | force_color: false, 556 | right: false, 557 | timezone: Some("Europe/London".to_string()), 558 | } 559 | ; "with timezone" 560 | )] 561 | #[test_case( 562 | vec![WriteLn("foo"), Sleep(300)], 563 | vec![Line("foo"), RightSpacerWithLondonTimezone], 564 | Args { 565 | after: 0.1, 566 | dash: '-', 567 | padding: 0, 568 | width: None, 569 | stopwatch: true, 570 | no_color: true, 571 | force_color: false, 572 | right: true, 573 | timezone: Some("Europe/London".to_string()), 574 | } 575 | ; "right spacer with timezone" 576 | )] 577 | #[test_case( 578 | vec![WriteLn("foo"), Sleep(300)], 579 | vec![Line("foo"), CustomWidthSpacer(20)], 580 | Args { 581 | after: 0.1, 582 | dash: '-', 583 | padding: 0, 584 | width: Some(20), 585 | stopwatch: false, 586 | no_color: true, 587 | force_color: false, 588 | right: false, 589 | timezone: None, 590 | } 591 | ; "custom width" 592 | )] 593 | 594 | fn test_output(ops: Vec, out: Vec, args: Args) -> Result<()> { 595 | let mut total_sleep_ms = 0; 596 | for op in ops.iter() { 597 | if let Sleep(duration) = op { 598 | total_sleep_ms += duration; 599 | } 600 | } 601 | 602 | let expected_wakeups = 2 + (total_sleep_ms as f64 / (args.after * 1000.0)).ceil() as usize; 603 | 604 | let input = BufReader::new(TimedInput::new(ops)); 605 | let (output, buffer_ref) = SharedBuffer::new(); 606 | let mut stats = super::TestStats::new(); 607 | run(input, output, args, Some(&mut stats))?; 608 | 609 | let output = String::from_utf8(buffer_ref.lock().unwrap().clone())?; 610 | let lines = output.lines().collect::>(); 611 | assert_eq!( 612 | lines.len(), 613 | out.len(), 614 | "wrong number of lines, expected {} got {:?}", 615 | out.len(), 616 | lines 617 | ); 618 | for (line, out) in lines.iter().zip(out.iter()) { 619 | match out { 620 | Line(expected) => assert_eq!(line, expected), 621 | Spacer => assert!(line.ends_with("----")), 622 | RightSpacer => assert!(line.starts_with("\r----")), 623 | SpacerWithLondonTimezone => { 624 | assert!(line.contains("GMT") || line.contains("BST")); 625 | assert!(line.ends_with("----")); 626 | } 627 | RightSpacerWithLondonTimezone => { 628 | assert!(line.contains("GMT") || line.contains("BST")); 629 | assert!(line.starts_with("\r----")); 630 | } 631 | CustomWidthSpacer(width) => { 632 | let dashes = "-".repeat(*width); 633 | let too_many_dashes = "-".repeat(*width + 1); 634 | assert!(line.contains(&dashes)); 635 | assert!(!line.contains(&too_many_dashes)); 636 | } 637 | } 638 | } 639 | 640 | assert!( 641 | // Allow some wiggle room in the expected wakeups. 642 | stats.wakeups <= (expected_wakeups * 2), 643 | "too many wakeups, expected {} got {}", 644 | expected_wakeups, 645 | stats.wakeups 646 | ); 647 | Ok(()) 648 | } 649 | } 650 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.1.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "android-tzdata" 31 | version = "0.1.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 34 | 35 | [[package]] 36 | name = "android_system_properties" 37 | version = "0.1.5" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 40 | dependencies = [ 41 | "libc", 42 | ] 43 | 44 | [[package]] 45 | name = "anstream" 46 | version = "0.6.18" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" 49 | dependencies = [ 50 | "anstyle", 51 | "anstyle-parse", 52 | "anstyle-query", 53 | "anstyle-wincon", 54 | "colorchoice", 55 | "is_terminal_polyfill", 56 | "utf8parse", 57 | ] 58 | 59 | [[package]] 60 | name = "anstyle" 61 | version = "1.0.10" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 64 | 65 | [[package]] 66 | name = "anstyle-parse" 67 | version = "0.2.6" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" 70 | dependencies = [ 71 | "utf8parse", 72 | ] 73 | 74 | [[package]] 75 | name = "anstyle-query" 76 | version = "1.1.2" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" 79 | dependencies = [ 80 | "windows-sys 0.59.0", 81 | ] 82 | 83 | [[package]] 84 | name = "anstyle-wincon" 85 | version = "3.0.6" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" 88 | dependencies = [ 89 | "anstyle", 90 | "windows-sys 0.59.0", 91 | ] 92 | 93 | [[package]] 94 | name = "anyhow" 95 | version = "1.0.95" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" 98 | 99 | [[package]] 100 | name = "autocfg" 101 | version = "1.4.0" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 104 | 105 | [[package]] 106 | name = "backtrace" 107 | version = "0.3.74" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 110 | dependencies = [ 111 | "addr2line", 112 | "cfg-if", 113 | "libc", 114 | "miniz_oxide", 115 | "object", 116 | "rustc-demangle", 117 | "windows-targets", 118 | ] 119 | 120 | [[package]] 121 | name = "bitflags" 122 | version = "2.7.0" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" 125 | 126 | [[package]] 127 | name = "bumpalo" 128 | version = "3.16.0" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 131 | 132 | [[package]] 133 | name = "cc" 134 | version = "1.2.7" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" 137 | dependencies = [ 138 | "shlex", 139 | ] 140 | 141 | [[package]] 142 | name = "cfg-if" 143 | version = "1.0.0" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 146 | 147 | [[package]] 148 | name = "chrono" 149 | version = "0.4.39" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" 152 | dependencies = [ 153 | "android-tzdata", 154 | "iana-time-zone", 155 | "js-sys", 156 | "num-traits", 157 | "wasm-bindgen", 158 | "windows-targets", 159 | ] 160 | 161 | [[package]] 162 | name = "chrono-tz" 163 | version = "0.10.0" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "cd6dd8046d00723a59a2f8c5f295c515b9bb9a331ee4f8f3d4dd49e428acd3b6" 166 | dependencies = [ 167 | "chrono", 168 | "chrono-tz-build", 169 | "phf", 170 | ] 171 | 172 | [[package]] 173 | name = "chrono-tz-build" 174 | version = "0.4.0" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "e94fea34d77a245229e7746bd2beb786cd2a896f306ff491fb8cecb3074b10a7" 177 | dependencies = [ 178 | "parse-zoneinfo", 179 | "phf_codegen", 180 | ] 181 | 182 | [[package]] 183 | name = "clap" 184 | version = "4.5.26" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" 187 | dependencies = [ 188 | "clap_builder", 189 | "clap_derive", 190 | ] 191 | 192 | [[package]] 193 | name = "clap_builder" 194 | version = "4.5.26" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" 197 | dependencies = [ 198 | "anstream", 199 | "anstyle", 200 | "clap_lex", 201 | "strsim", 202 | ] 203 | 204 | [[package]] 205 | name = "clap_derive" 206 | version = "4.5.24" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" 209 | dependencies = [ 210 | "heck", 211 | "proc-macro2", 212 | "quote", 213 | "syn", 214 | ] 215 | 216 | [[package]] 217 | name = "clap_lex" 218 | version = "0.7.4" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 221 | 222 | [[package]] 223 | name = "colorchoice" 224 | version = "1.0.3" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 227 | 228 | [[package]] 229 | name = "core-foundation-sys" 230 | version = "0.8.7" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 233 | 234 | [[package]] 235 | name = "env_filter" 236 | version = "0.1.3" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" 239 | dependencies = [ 240 | "log", 241 | "regex", 242 | ] 243 | 244 | [[package]] 245 | name = "env_logger" 246 | version = "0.11.6" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" 249 | dependencies = [ 250 | "anstream", 251 | "anstyle", 252 | "env_filter", 253 | "humantime", 254 | "log", 255 | ] 256 | 257 | [[package]] 258 | name = "equivalent" 259 | version = "1.0.1" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 262 | 263 | [[package]] 264 | name = "errno" 265 | version = "0.3.10" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 268 | dependencies = [ 269 | "libc", 270 | "windows-sys 0.59.0", 271 | ] 272 | 273 | [[package]] 274 | name = "getrandom" 275 | version = "0.2.15" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 278 | dependencies = [ 279 | "cfg-if", 280 | "libc", 281 | "wasi", 282 | ] 283 | 284 | [[package]] 285 | name = "gimli" 286 | version = "0.31.1" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 289 | 290 | [[package]] 291 | name = "hashbrown" 292 | version = "0.15.2" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 295 | 296 | [[package]] 297 | name = "heck" 298 | version = "0.5.0" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 301 | 302 | [[package]] 303 | name = "hermit-abi" 304 | version = "0.4.0" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" 307 | 308 | [[package]] 309 | name = "human-panic" 310 | version = "2.0.2" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "80b84a66a325082740043a6c28bbea400c129eac0d3a27673a1de971e44bf1f7" 313 | dependencies = [ 314 | "anstream", 315 | "anstyle", 316 | "backtrace", 317 | "os_info", 318 | "serde", 319 | "serde_derive", 320 | "toml", 321 | "uuid", 322 | ] 323 | 324 | [[package]] 325 | name = "humantime" 326 | version = "2.1.0" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 329 | 330 | [[package]] 331 | name = "iana-time-zone" 332 | version = "0.1.61" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 335 | dependencies = [ 336 | "android_system_properties", 337 | "core-foundation-sys", 338 | "iana-time-zone-haiku", 339 | "js-sys", 340 | "wasm-bindgen", 341 | "windows-core", 342 | ] 343 | 344 | [[package]] 345 | name = "iana-time-zone-haiku" 346 | version = "0.1.2" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 349 | dependencies = [ 350 | "cc", 351 | ] 352 | 353 | [[package]] 354 | name = "indexmap" 355 | version = "2.7.0" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" 358 | dependencies = [ 359 | "equivalent", 360 | "hashbrown", 361 | ] 362 | 363 | [[package]] 364 | name = "is-terminal" 365 | version = "0.4.13" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" 368 | dependencies = [ 369 | "hermit-abi", 370 | "libc", 371 | "windows-sys 0.52.0", 372 | ] 373 | 374 | [[package]] 375 | name = "is_ci" 376 | version = "1.2.0" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" 379 | 380 | [[package]] 381 | name = "is_terminal_polyfill" 382 | version = "1.70.1" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 385 | 386 | [[package]] 387 | name = "js-sys" 388 | version = "0.3.76" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" 391 | dependencies = [ 392 | "once_cell", 393 | "wasm-bindgen", 394 | ] 395 | 396 | [[package]] 397 | name = "lazy_static" 398 | version = "1.5.0" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 401 | 402 | [[package]] 403 | name = "libc" 404 | version = "0.2.169" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 407 | 408 | [[package]] 409 | name = "linux-raw-sys" 410 | version = "0.4.15" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 413 | 414 | [[package]] 415 | name = "log" 416 | version = "0.4.22" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 419 | 420 | [[package]] 421 | name = "memchr" 422 | version = "2.7.4" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 425 | 426 | [[package]] 427 | name = "miniz_oxide" 428 | version = "0.8.2" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" 431 | dependencies = [ 432 | "adler2", 433 | ] 434 | 435 | [[package]] 436 | name = "num-traits" 437 | version = "0.2.19" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 440 | dependencies = [ 441 | "autocfg", 442 | ] 443 | 444 | [[package]] 445 | name = "object" 446 | version = "0.36.7" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 449 | dependencies = [ 450 | "memchr", 451 | ] 452 | 453 | [[package]] 454 | name = "once_cell" 455 | version = "1.20.2" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 458 | 459 | [[package]] 460 | name = "os_info" 461 | version = "3.9.2" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "6e6520c8cc998c5741ee68ec1dc369fc47e5f0ea5320018ecf2a1ccd6328f48b" 464 | dependencies = [ 465 | "log", 466 | "serde", 467 | "windows-sys 0.52.0", 468 | ] 469 | 470 | [[package]] 471 | name = "owo-colors" 472 | version = "4.1.0" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" 475 | dependencies = [ 476 | "supports-color 2.1.0", 477 | "supports-color 3.0.2", 478 | ] 479 | 480 | [[package]] 481 | name = "parse-zoneinfo" 482 | version = "0.3.1" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" 485 | dependencies = [ 486 | "regex", 487 | ] 488 | 489 | [[package]] 490 | name = "phf" 491 | version = "0.11.3" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" 494 | dependencies = [ 495 | "phf_shared", 496 | ] 497 | 498 | [[package]] 499 | name = "phf_codegen" 500 | version = "0.11.3" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" 503 | dependencies = [ 504 | "phf_generator", 505 | "phf_shared", 506 | ] 507 | 508 | [[package]] 509 | name = "phf_generator" 510 | version = "0.11.3" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" 513 | dependencies = [ 514 | "phf_shared", 515 | "rand", 516 | ] 517 | 518 | [[package]] 519 | name = "phf_shared" 520 | version = "0.11.3" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" 523 | dependencies = [ 524 | "siphasher", 525 | ] 526 | 527 | [[package]] 528 | name = "proc-macro2" 529 | version = "1.0.92" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" 532 | dependencies = [ 533 | "unicode-ident", 534 | ] 535 | 536 | [[package]] 537 | name = "quote" 538 | version = "1.0.38" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 541 | dependencies = [ 542 | "proc-macro2", 543 | ] 544 | 545 | [[package]] 546 | name = "rand" 547 | version = "0.8.5" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 550 | dependencies = [ 551 | "rand_core", 552 | ] 553 | 554 | [[package]] 555 | name = "rand_core" 556 | version = "0.6.4" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 559 | 560 | [[package]] 561 | name = "regex" 562 | version = "1.11.1" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 565 | dependencies = [ 566 | "aho-corasick", 567 | "memchr", 568 | "regex-automata", 569 | "regex-syntax", 570 | ] 571 | 572 | [[package]] 573 | name = "regex-automata" 574 | version = "0.4.9" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 577 | dependencies = [ 578 | "aho-corasick", 579 | "memchr", 580 | "regex-syntax", 581 | ] 582 | 583 | [[package]] 584 | name = "regex-syntax" 585 | version = "0.8.5" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 588 | 589 | [[package]] 590 | name = "rustc-demangle" 591 | version = "0.1.24" 592 | source = "registry+https://github.com/rust-lang/crates.io-index" 593 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 594 | 595 | [[package]] 596 | name = "rustix" 597 | version = "0.38.43" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" 600 | dependencies = [ 601 | "bitflags", 602 | "errno", 603 | "libc", 604 | "linux-raw-sys", 605 | "windows-sys 0.59.0", 606 | ] 607 | 608 | [[package]] 609 | name = "serde" 610 | version = "1.0.217" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" 613 | dependencies = [ 614 | "serde_derive", 615 | ] 616 | 617 | [[package]] 618 | name = "serde_derive" 619 | version = "1.0.217" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" 622 | dependencies = [ 623 | "proc-macro2", 624 | "quote", 625 | "syn", 626 | ] 627 | 628 | [[package]] 629 | name = "serde_spanned" 630 | version = "0.6.8" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 633 | dependencies = [ 634 | "serde", 635 | ] 636 | 637 | [[package]] 638 | name = "shlex" 639 | version = "1.3.0" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 642 | 643 | [[package]] 644 | name = "siphasher" 645 | version = "1.0.1" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" 648 | 649 | [[package]] 650 | name = "spacer" 651 | version = "0.4.5" 652 | dependencies = [ 653 | "anyhow", 654 | "chrono", 655 | "chrono-tz", 656 | "clap", 657 | "env_logger", 658 | "human-panic", 659 | "lazy_static", 660 | "log", 661 | "owo-colors", 662 | "terminal_size", 663 | "test-case", 664 | ] 665 | 666 | [[package]] 667 | name = "strsim" 668 | version = "0.11.1" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 671 | 672 | [[package]] 673 | name = "supports-color" 674 | version = "2.1.0" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89" 677 | dependencies = [ 678 | "is-terminal", 679 | "is_ci", 680 | ] 681 | 682 | [[package]] 683 | name = "supports-color" 684 | version = "3.0.2" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" 687 | dependencies = [ 688 | "is_ci", 689 | ] 690 | 691 | [[package]] 692 | name = "syn" 693 | version = "2.0.96" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" 696 | dependencies = [ 697 | "proc-macro2", 698 | "quote", 699 | "unicode-ident", 700 | ] 701 | 702 | [[package]] 703 | name = "terminal_size" 704 | version = "0.4.1" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" 707 | dependencies = [ 708 | "rustix", 709 | "windows-sys 0.59.0", 710 | ] 711 | 712 | [[package]] 713 | name = "test-case" 714 | version = "3.3.1" 715 | source = "registry+https://github.com/rust-lang/crates.io-index" 716 | checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" 717 | dependencies = [ 718 | "test-case-macros", 719 | ] 720 | 721 | [[package]] 722 | name = "test-case-core" 723 | version = "3.3.1" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" 726 | dependencies = [ 727 | "cfg-if", 728 | "proc-macro2", 729 | "quote", 730 | "syn", 731 | ] 732 | 733 | [[package]] 734 | name = "test-case-macros" 735 | version = "3.3.1" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" 738 | dependencies = [ 739 | "proc-macro2", 740 | "quote", 741 | "syn", 742 | "test-case-core", 743 | ] 744 | 745 | [[package]] 746 | name = "toml" 747 | version = "0.8.19" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" 750 | dependencies = [ 751 | "serde", 752 | "serde_spanned", 753 | "toml_datetime", 754 | "toml_edit", 755 | ] 756 | 757 | [[package]] 758 | name = "toml_datetime" 759 | version = "0.6.8" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" 762 | dependencies = [ 763 | "serde", 764 | ] 765 | 766 | [[package]] 767 | name = "toml_edit" 768 | version = "0.22.22" 769 | source = "registry+https://github.com/rust-lang/crates.io-index" 770 | checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" 771 | dependencies = [ 772 | "indexmap", 773 | "serde", 774 | "serde_spanned", 775 | "toml_datetime", 776 | ] 777 | 778 | [[package]] 779 | name = "unicode-ident" 780 | version = "1.0.14" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 783 | 784 | [[package]] 785 | name = "utf8parse" 786 | version = "0.2.2" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 789 | 790 | [[package]] 791 | name = "uuid" 792 | version = "1.11.1" 793 | source = "registry+https://github.com/rust-lang/crates.io-index" 794 | checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4" 795 | dependencies = [ 796 | "getrandom", 797 | ] 798 | 799 | [[package]] 800 | name = "wasi" 801 | version = "0.11.0+wasi-snapshot-preview1" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 804 | 805 | [[package]] 806 | name = "wasm-bindgen" 807 | version = "0.2.99" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" 810 | dependencies = [ 811 | "cfg-if", 812 | "once_cell", 813 | "wasm-bindgen-macro", 814 | ] 815 | 816 | [[package]] 817 | name = "wasm-bindgen-backend" 818 | version = "0.2.99" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" 821 | dependencies = [ 822 | "bumpalo", 823 | "log", 824 | "proc-macro2", 825 | "quote", 826 | "syn", 827 | "wasm-bindgen-shared", 828 | ] 829 | 830 | [[package]] 831 | name = "wasm-bindgen-macro" 832 | version = "0.2.99" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" 835 | dependencies = [ 836 | "quote", 837 | "wasm-bindgen-macro-support", 838 | ] 839 | 840 | [[package]] 841 | name = "wasm-bindgen-macro-support" 842 | version = "0.2.99" 843 | source = "registry+https://github.com/rust-lang/crates.io-index" 844 | checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" 845 | dependencies = [ 846 | "proc-macro2", 847 | "quote", 848 | "syn", 849 | "wasm-bindgen-backend", 850 | "wasm-bindgen-shared", 851 | ] 852 | 853 | [[package]] 854 | name = "wasm-bindgen-shared" 855 | version = "0.2.99" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" 858 | 859 | [[package]] 860 | name = "windows-core" 861 | version = "0.52.0" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 864 | dependencies = [ 865 | "windows-targets", 866 | ] 867 | 868 | [[package]] 869 | name = "windows-sys" 870 | version = "0.52.0" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 873 | dependencies = [ 874 | "windows-targets", 875 | ] 876 | 877 | [[package]] 878 | name = "windows-sys" 879 | version = "0.59.0" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 882 | dependencies = [ 883 | "windows-targets", 884 | ] 885 | 886 | [[package]] 887 | name = "windows-targets" 888 | version = "0.52.6" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 891 | dependencies = [ 892 | "windows_aarch64_gnullvm", 893 | "windows_aarch64_msvc", 894 | "windows_i686_gnu", 895 | "windows_i686_gnullvm", 896 | "windows_i686_msvc", 897 | "windows_x86_64_gnu", 898 | "windows_x86_64_gnullvm", 899 | "windows_x86_64_msvc", 900 | ] 901 | 902 | [[package]] 903 | name = "windows_aarch64_gnullvm" 904 | version = "0.52.6" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 907 | 908 | [[package]] 909 | name = "windows_aarch64_msvc" 910 | version = "0.52.6" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 913 | 914 | [[package]] 915 | name = "windows_i686_gnu" 916 | version = "0.52.6" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 919 | 920 | [[package]] 921 | name = "windows_i686_gnullvm" 922 | version = "0.52.6" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 925 | 926 | [[package]] 927 | name = "windows_i686_msvc" 928 | version = "0.52.6" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 931 | 932 | [[package]] 933 | name = "windows_x86_64_gnu" 934 | version = "0.52.6" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 937 | 938 | [[package]] 939 | name = "windows_x86_64_gnullvm" 940 | version = "0.52.6" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 943 | 944 | [[package]] 945 | name = "windows_x86_64_msvc" 946 | version = "0.52.6" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 949 | --------------------------------------------------------------------------------