├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── README.md ├── src ├── http.rs ├── output.rs ├── bin │ └── gfs.rs ├── binary_round.rs ├── ifs.rs ├── lib.rs └── gfs.rs ├── Cargo.toml ├── dataset-docs └── noaa-gfs-analysis-hourly.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /data 3 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "rust-lang.rust-analyzer", 4 | "serayuzgur.crates", 5 | "tamasfe.even-better-toml" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave": true, 3 | "rust-analyzer.check.command": "clippy", 4 | "rust-analyzer.check.extraArgs": [ 5 | "--", 6 | "-W", 7 | "clippy::pedantic", 8 | "-A", 9 | "clippy::missing_errors_doc", 10 | "-A", 11 | "clippy::missing_panics_doc" 12 | ], 13 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Superceeded by https://github.com/dynamical-org/reformatters/ 2 | 3 | # Reformatters 4 | 5 | Reformatting code to create dynamical.org zarr archives from source weather and climate datasets. 6 | 7 | Heavily in progress work. 8 | 9 | ### Development 10 | 11 | 1. [Install rust](https://www.rust-lang.org/tools/install) 12 | 1. [Install GDAL](https://gdal.org/download.html) (eg. `sudo apt install -y libgdal-dev` or `brew install gdal`) 13 | - If `cargo run` gives an error about a missing gdal library, make sure the library is on your LD_LIBRARY_PATH 14 | 1. Example command: `cargo run --bin gfs -- backfill temperature_2m 2024-01-01T00:00:00Z 2024-02-01T00:00:00Z 2024-02-01T00:00:00Z file://path-to-your-directory` 15 | -------------------------------------------------------------------------------- /src/http.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; 3 | use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware}; 4 | use std::time::Duration; 5 | 6 | pub type Client = ClientWithMiddleware; 7 | 8 | pub fn client() -> Result { 9 | let client = reqwest::Client::builder() 10 | .timeout(Duration::from_secs(45)) 11 | .connect_timeout(Duration::from_secs(5)) 12 | .redirect(reqwest::redirect::Policy::none()) 13 | .https_only(true) 14 | .build()?; 15 | 16 | let backoff_policy = ExponentialBackoff::builder().build_with_max_retries(32); 17 | 18 | Ok(ClientBuilder::new(client) 19 | .with(RetryTransientMiddleware::new_with_policy(backoff_policy)) 20 | .build()) 21 | } 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "reformatters" 3 | version = "0.1.0" 4 | edition = "2021" 5 | default-run = "gfs" 6 | 7 | [dependencies] 8 | anyhow = "1" 9 | blosc = "0" 10 | cached = "0" 11 | console-subscriber = "0" 12 | chrono = { version = "0", features = ["now"] } 13 | futures = "0" 14 | gdal = { version = "0", features = ["array"] } 15 | gdal-sys = { version = "0", features = ["bindgen"] } 16 | itertools = "0" 17 | ndarray = "0" 18 | object_store = { version = "0", features = ["aws"] } 19 | once_cell = "1" 20 | regex = "1" 21 | reqwest = { version = "0", features = ["http2", "stream"] } 22 | reqwest-retry = "0" 23 | reqwest-middleware = "0" 24 | serde = { version = "1.0", features = ["derive"] } 25 | serde_json = "1.0" 26 | tokio = { version = "1", features = ["full", "tracing"] } 27 | tokio-util = { version = "0" } 28 | clap = { version = "4.5.7", features = ["derive"] } 29 | backon = "0" 30 | ndarray-interp = "0" 31 | url = "2.5.2" 32 | -------------------------------------------------------------------------------- /src/output.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use std::path::Path; 3 | 4 | use anyhow::{anyhow, bail, Result}; 5 | use object_store::{aws::AmazonS3Builder, local::LocalFileSystem}; 6 | use url::Url; 7 | 8 | pub type Storage = Arc; 9 | pub type PutPayload = object_store::PutPayload; 10 | pub type PutResult = object_store::PutResult; 11 | 12 | pub fn get_object_store(dest: &str) -> Result { 13 | let url = Url::parse(dest)?; 14 | println!("URL: {url}"); 15 | match url.scheme() { 16 | "file" => { 17 | if url.host().is_some() { 18 | bail!("Unsupported file url. Expected no host: {}", url.as_str()); 19 | } 20 | let path = Path::new(url.path()); 21 | Ok(Arc::new(LocalFileSystem::new_with_prefix(path)?)) 22 | } 23 | "s3" => { 24 | let bucket_name = url.host().ok_or(anyhow!("Invalid bucket_name"))?; 25 | let store = AmazonS3Builder::from_env() 26 | .with_bucket_name(bucket_name.to_string()) 27 | .build()?; 28 | Ok(Arc::new(store)) 29 | } 30 | "gcs" => { 31 | bail!("GCS not implemented") 32 | } 33 | "azure" => { 34 | bail!("Azure not implemented") 35 | } 36 | _ => bail!("Unsupported url. Try file:/foo, s3://bucket"), 37 | } 38 | } 39 | 40 | // fn first_env_var(var1: &str, var2: &str) -> Result { 41 | // Ok(std::env::var(var1).or_else(|_| std::env::var(var2))?) 42 | // } 43 | -------------------------------------------------------------------------------- /src/bin/gfs.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use chrono::{DateTime, Utc}; 3 | use clap::{Parser, Subcommand}; 4 | 5 | #[derive(clap::Parser, Debug)] 6 | struct Cli { 7 | #[clap(subcommand)] 8 | cmd: SubCommand, 9 | } 10 | 11 | #[derive(Subcommand, Debug)] 12 | enum SubCommand { 13 | /// Ingest data for all variables over the date range, using the end of the date 14 | /// range as the end date of the dataset 15 | Update { 16 | /// Earliest timestamp to ingest 17 | ingest_min_date: DateTime, 18 | 19 | /// Most recent timestamp to ingest (inclusive) 20 | ingest_max_date: DateTime, 21 | 22 | /// URL string specifying the root of the zarr to create. eg. s3://bucket/path.zarr file:///home/.../path.zarr 23 | destination: String, 24 | }, 25 | /// Backfill data for a specific variable over a date range 26 | Backfill { 27 | /// Data variable name to ingest 28 | variable: String, 29 | 30 | /// Earliest timestamp to ingest 31 | ingest_min_date: DateTime, 32 | 33 | /// Most recent timestamp to ingest (inclusive) 34 | ingest_max_date: DateTime, 35 | 36 | /// Most recent timestamp of the entire dataset 37 | dataset_max_date: DateTime, 38 | 39 | /// URL string specifying the root of the zarr to create. eg. s3://bucket/path.zarr file:///home/.../path.zarr 40 | destination: String, 41 | 42 | /// Don't write metadata, just write chunks 43 | skip_metadata: Option, 44 | }, 45 | } 46 | 47 | #[tokio::main] 48 | async fn main() -> Result<()> { 49 | let cli = Cli::try_parse()?; 50 | 51 | match cli.cmd { 52 | SubCommand::Backfill { 53 | variable, 54 | ingest_min_date, 55 | ingest_max_date, 56 | dataset_max_date, 57 | destination, 58 | skip_metadata, 59 | } => { 60 | reformatters::gfs::reformat( 61 | variable, 62 | ingest_min_date, 63 | ingest_max_date, 64 | dataset_max_date, 65 | destination, 66 | skip_metadata.unwrap_or(false), 67 | ) 68 | .await 69 | } 70 | SubCommand::Update { 71 | ingest_min_date, 72 | ingest_max_date, 73 | destination, 74 | } => { 75 | for variable_name in [ 76 | "temperature_2m", 77 | "precipitation_surface", 78 | "wind_u_10m", 79 | "wind_v_10m", 80 | ] { 81 | reformatters::gfs::reformat( 82 | variable_name.to_string(), 83 | ingest_min_date, 84 | ingest_max_date, 85 | ingest_max_date, 86 | destination.clone(), 87 | false, 88 | ) 89 | .await?; 90 | } 91 | Ok(()) 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /dataset-docs/noaa-gfs-analysis-hourly.md: -------------------------------------------------------------------------------- 1 | # NOAA GFS analysis, hourly 2 | 3 | Historical weather data from the Global Forecast System (GFS) model operated by NOAA NCEP, transformed into zarr format by [dynamical.org](https://dynamical.org). 4 | 5 | ## Dataset Overview 6 | 7 | - **Spatial domain**: Global 8 | - **Spatial resolution**: 0.25 degrees (~20km) 9 | - **Time domain**: 2015-01-15 00:00:00 UTC to 2024-07-01 00:00:00 UTC 10 | - **Time resolution**: 1 hour 11 | 12 | URL: `https://data.dynamical.org/noaa/gfs/analysis-hourly/latest.zarr?email=optional@email.com` 13 | 14 | _Email optional. Providing your email as a query param helps us understand usage and impact to keep dynamical.org supported for the long-term. For catalog updates follow [here](https://dynamical.org/updates)._ 15 | 16 | ## Description 17 | 18 | The Global Forecast System (GFS) is a National Oceanic and Atmospheric Administration (NOAA) National Centers for Environmental Prediction (NCEP) weather forecast model that generates data for dozens of atmospheric and land-soil variables, including temperatures, winds, precipitation, soil moisture, and atmospheric ozone concentration. The system couples four separate models (atmosphere, ocean model, land/soil model, and sea ice) that work together to depict weather conditions. 19 | 20 | This dataset is an "analysis" containing the model's best estimate of each value at each timestep. In other words, it does not contain a forecast dimension. GFS starts a new model run every 6 hours and dynamical.org has created this analysis by concatenating the first 6 hours of each forecast. Before 2021-02-27 GFS had a 3 hourly step at early forecast hours. In this reanalysis we have used linear interpolation in the time dimension to fill in the two timesteps between the three-hourly values prior to 2021-02-27. 21 | 22 | The data values in this dataset have been rounded in their binary representation to improve compression. We round to retain 9 bits of the floating point number's mantissa (a 10 digit significand) which creates a maximum of 0.2% difference between the original and rounded value. See [Klöwer et al. 2021](https://www.nature.com/articles/s43588-021-00156-2) for more information. 23 | 24 | A hearty thank you to Source Cooperative for hosting this dataset. 25 | 26 | ## Dimensions 27 | 28 | | Dimension | Start | Stop | Units | 29 | | --------- | ----------------------- | ----------------------- | --------------------------------- | 30 | | latitude | 90 | -90 | decimal degrees | 31 | | longitude | -180 | 180 | decimal degrees | 32 | | time | 2015-01-15 00:00:00 UTC | 2024-07-01 00:00:00 UTC | seconds since 1970-01-01 00:00:00 | 33 | 34 | ## Variables 35 | 36 | | Variable | Units | Dimensions | Description | 37 | | --------------------- | ---------- | --------------------------- | ---------------------------------------------------- | 38 | | precipitation_surface | kg/(m^2 s) | time × latitude × longitude | Precipitation rate at earth surface | 39 | | temperature_2m | C | time × latitude × longitude | Temperature 2 meters above earth surface | 40 | | wind_u_10m | m/s | time × latitude × longitude | Wind speed u-component 10 meters above earth surface | 41 | | wind_v_10m | m/s | time × latitude × longitude | Wind speed v-component 10 meters above earth surface | 42 | 43 | ## Example 44 | 45 | ### Mean temperature for a single day 46 | 47 | ```python 48 | import xarray as xr 49 | 50 | ds = xr.open_zarr("https://data.dynamical.org/noaa/gfs/analysis-hourly/latest.json?email=optional@email.com") 51 | ds["temperature_2m"].sel(time="2024-06-01T00:00").mean().compute() 52 | ``` 53 | -------------------------------------------------------------------------------- /src/binary_round.rs: -------------------------------------------------------------------------------- 1 | #[must_use] 2 | pub fn round(value: f32, keep_mantissa_bits: u32) -> f32 { 3 | // The IEEE 754 standard specifies a binary32 as having: 4 | // Sign bit: 1 bit 5 | // Exponent width: 8 bits 6 | // Significand precision: 24 bits (23 explicitly stored) 7 | // 8 | const MANTISSA_BITS: u32 = 23; 9 | const MANTISSA_MASK: u32 = trailing_1s(MANTISSA_BITS); 10 | const EXPONENT_MASK: u32 = trailing_1s(8) << (32 - 8 - 1); 11 | const SIGN_MASK: u32 = 1 << 31; 12 | 13 | if keep_mantissa_bits >= MANTISSA_BITS || value.is_nan() || value.is_infinite() { 14 | return value; 15 | } 16 | 17 | let bits = value.to_bits(); 18 | 19 | // The number of trailing bits we'll be setting to zero 20 | let drop_bits = MANTISSA_BITS - keep_mantissa_bits; 21 | 22 | // Extract portions of the number's bits 23 | // Rounding mantissa to the n-th place (ie n == keep_bits_mantissa): 24 | let sign = bits & SIGN_MASK; 25 | let mut exponent = bits & EXPONENT_MASK; 26 | let mut mantissa = bits & MANTISSA_MASK; 27 | let round_bit = bits & (1 << drop_bits); // n-th place - the smallest bit remaining after rounding 28 | let half_bit = bits & (1 << (drop_bits - 1)); // n+1 th - the bit directly following round bit 29 | let sticky_bits = bits & trailing_1s(drop_bits - 1); // nth + 2+ - the rest of the bits after half bit 30 | 31 | // Via the general rule at the bottom of this post 32 | // https://angularindepth.com/posts/1017/how-to-round-binary-numbers 33 | mantissa = match (half_bit == 0, sticky_bits == 0) { 34 | (true, _) => round_down(mantissa, drop_bits), 35 | (false, false) => round_up(mantissa, drop_bits), 36 | (false, true) => round_to_even(mantissa, drop_bits, round_bit), 37 | }; 38 | 39 | // If mantissa overflows, increment exponent and truncate overflow so mantissa wraps back to zero 40 | if mantissa > MANTISSA_MASK { 41 | exponent += 1 << MANTISSA_BITS; // add 1 to exponent 42 | mantissa &= MANTISSA_MASK; // zero out overflowed bits 43 | 44 | // if exponent overflows, return infinity 45 | // all 1s exponent has a special meaning, so it's also invalid as a numeric value 46 | if exponent >= EXPONENT_MASK { 47 | let sign_is_positive = sign == 0; // zero means positive, 1 means negative 48 | return if sign_is_positive { 49 | f32::INFINITY 50 | } else { 51 | f32::NEG_INFINITY 52 | }; 53 | } 54 | } 55 | 56 | f32::from_bits(sign | exponent | mantissa) 57 | } 58 | 59 | fn round_down(value: u32, drop_bits: u32) -> u32 { 60 | (value >> drop_bits) << drop_bits 61 | } 62 | 63 | fn round_up(value: u32, drop_bits: u32) -> u32 { 64 | let value = round_down(value, drop_bits); 65 | let increment = 1 << drop_bits; 66 | value + increment 67 | } 68 | 69 | /// Tie breaking round using the "ties to even" logic. 70 | /// See 71 | fn round_to_even(value: u32, drop_bits: u32, round_bit: u32) -> u32 { 72 | // if bit we're rounding to is already 0, then rounding up would make it 1 (odd) 73 | // so we round down and leave it zero (even). 74 | if round_bit == 0 { 75 | round_down(value, drop_bits) 76 | } else { 77 | round_up(value, drop_bits) 78 | } 79 | } 80 | 81 | const fn trailing_1s(num_ones: u32) -> u32 { 82 | (1 << num_ones) - 1 83 | } 84 | 85 | #[cfg(test)] 86 | #[allow( 87 | clippy::float_cmp, 88 | clippy::unusual_byte_groupings, 89 | clippy::unreadable_literal 90 | )] 91 | mod tests { 92 | use super::*; 93 | 94 | #[test] 95 | fn round_up_no_tie_positive() { 96 | // sign exponent mantissa 97 | // - -------- ----------------------- 98 | let original = f32::from_bits(0b0_10000000_10101010101010101010101); 99 | assert_eq!(original, 3.3333333_f32); 100 | 101 | let rounded = round(original, 4); 102 | 103 | assert!(rounded > original); // should have rounded up 104 | let expected = f32::from_bits(0b0_10000000_10110000000000000000000); 105 | assert_eq!(rounded, expected); 106 | assert_eq!(rounded, 3.375); 107 | } 108 | 109 | #[test] 110 | fn round_up_no_tie_negative() { 111 | let original = f32::from_bits(0b1_10000000_10101010101010101010101); 112 | assert_eq!(original, -3.3333333_f32); 113 | 114 | let rounded = round(original, 4); 115 | 116 | let expected = f32::from_bits(0b1_10000000_10110000000000000000000); 117 | assert_eq!(rounded, expected); 118 | assert_eq!(rounded, -3.375); 119 | 120 | assert!(rounded < original); // "up" actually means "away from zero" 121 | 122 | // check that rounding down would actually have gotten you farther away 123 | let rounded_down = f32::from_bits(0b1_10000000_10100000000000000000000); 124 | assert!((original - expected).abs() < (original - rounded_down).abs()); 125 | } 126 | 127 | #[test] 128 | fn round_down_no_tie_positive() { 129 | let original = f32::from_bits(0b0_10000011_10101010101010101010101); 130 | assert_eq!(original, 26.666666); 131 | 132 | let rounded = round(original, 5); 133 | 134 | let expected = f32::from_bits(0b0_10000011_10101000000000000000000); 135 | assert_eq!(rounded, expected); 136 | assert_eq!(rounded, 26.5); 137 | 138 | assert!(rounded < original); 139 | 140 | let rounded_up = f32::from_bits(0b0_10000011_10111000000000000000000); 141 | // check that rounding up would actually have gotten you farther away 142 | assert!((original - expected).abs() < (original - rounded_up).abs()); 143 | } 144 | 145 | #[test] 146 | fn round_down_no_tie_negative() { 147 | let original = f32::from_bits(0b1_10000011_10101010101010101010101); 148 | assert_eq!(original, -26.666666); 149 | 150 | let rounded = round(original, 5); 151 | 152 | let expected = f32::from_bits(0b1_10000011_10101000000000000000000); 153 | assert_eq!(rounded, expected); 154 | assert_eq!(rounded, -26.5); 155 | 156 | assert!(rounded > original); 157 | 158 | let rounded_up = f32::from_bits(0b1_10000011_10111000000000000000000); 159 | // check that rounding up would actually have gotten you farther away 160 | assert!((original - expected).abs() < (original - rounded_up).abs()); 161 | } 162 | 163 | #[test] 164 | fn round_tie_down_to_even_positive() { 165 | let original = f32::from_bits(0b0_10000010_00100000000000000000000); 166 | // half bit is 1 ^, all else is zeros 167 | assert_eq!(original, 9.); 168 | 169 | let rounded = round(original, 2); 170 | 171 | let expected = f32::from_bits(0b0_10000010_00000000000000000000000); 172 | assert_eq!(rounded, expected); 173 | assert_eq!(rounded, 8.); 174 | 175 | assert!(rounded < original); 176 | 177 | let rounded_up = f32::from_bits(0b0_10000010_01000000000000000000000); 178 | // check that rounding up would actually have gotten you farther away 179 | assert!((original - expected).abs() == (original - rounded_up).abs()); 180 | } 181 | 182 | #[test] 183 | fn round_tie_up_to_even_positive() { 184 | let original = f32::from_bits(0b0_10000010_00110000000000000000000); 185 | assert_eq!(original, 9.5); 186 | 187 | let rounded = round(original, 3); 188 | 189 | let expected = f32::from_bits(0b0_10000010_01000000000000000000000); 190 | assert_eq!(rounded, expected); 191 | assert_eq!(rounded, 10.); 192 | 193 | assert!(rounded > original); 194 | 195 | let rounded_down = f32::from_bits(0b0_10000010_00100000000000000000000); 196 | // check that rounding up would actually have gotten you farther away 197 | assert!((original - expected).abs() == (original - rounded_down).abs()); 198 | } 199 | 200 | #[test] 201 | fn round_keep_all_bits() { 202 | let original = f32::from_bits(0b0_10000001_10000000000000000000111); 203 | assert_eq!(original, 6.0000033); 204 | 205 | let rounded = round(original, 23); // keep all bits, not rounding 206 | 207 | assert_eq!(rounded, original); 208 | } 209 | 210 | #[test] 211 | fn round_keep_all_but_one_bits_trailing_1() { 212 | let original = f32::from_bits(0b0_10000001_10000000000000000000111); 213 | assert_eq!(original, 6.0000033); 214 | 215 | let rounded = round(original, 22); 216 | 217 | let expected = f32::from_bits(0b0_10000001_10000000000000000001000); 218 | assert_eq!(rounded, expected); 219 | } 220 | #[test] 221 | fn round_keep_all_but_one_bits_trailing_0() { 222 | let original = f32::from_bits(0b0_10000001_10000000000000000000110); 223 | assert_eq!(original, 6.000003); 224 | 225 | let rounded = round(original, 22); 226 | 227 | let expected = f32::from_bits(0b0_10000001_10000000000000000000110); 228 | assert_eq!(rounded, expected); 229 | } 230 | #[test] 231 | fn round_mantissa_overflow() { 232 | let original = f32::from_bits(0b0_10000001_11111111111111111111111); 233 | assert_eq!(original, 7.9999995); 234 | 235 | let rounded = round(original, 15); 236 | 237 | let expected = f32::from_bits(0b0_10000010_00000000000000000000000); 238 | assert_eq!(rounded, 8.); 239 | assert_eq!(rounded, expected); 240 | } 241 | #[test] 242 | fn round_exponent_and_mantissa_overflow_positive() { 243 | let original = f32::from_bits(0b0_11111110_11111111111111111111111); 244 | 245 | let rounded = round(original, 20); 246 | 247 | assert_eq!(rounded, f32::INFINITY); 248 | } 249 | #[test] 250 | fn round_exponent_and_mantissa_overflow_negative() { 251 | let original = f32::from_bits(0b1_11111110_11111111111111111111111); 252 | 253 | let rounded = round(original, 20); 254 | 255 | assert_eq!(rounded, f32::NEG_INFINITY); 256 | } 257 | #[test] 258 | fn round_zero() { 259 | let original: f32 = 0.; 260 | 261 | let rounded = round(original, 20); 262 | 263 | assert_eq!(rounded, 0.); 264 | } 265 | #[test] 266 | fn round_subnormal_to_normal() { 267 | let original = f32::from_bits(0b0_00000000_11111111111111111111111); 268 | assert_eq!(original, 1.1754942e-38); 269 | 270 | let rounded = round(original, 20); 271 | 272 | let expected = f32::from_bits(0b0_00000001_00000000000000000000000); 273 | assert_eq!(expected, 1.1754944e-38); 274 | 275 | assert_eq!(rounded, expected); 276 | } 277 | #[test] 278 | fn round_subnormal_to_subnormal() { 279 | let original = f32::from_bits(0b0_00000000_00011111111111111111111); 280 | assert_eq!(original, 1.469367e-39); 281 | 282 | let rounded = round(original, 3); 283 | 284 | let expected = f32::from_bits(0b0_00000000_00100000000000000000000); 285 | assert_eq!(expected, 1.469368e-39); 286 | 287 | assert_eq!(rounded, expected); 288 | } 289 | #[test] 290 | fn print_diffs() { 291 | let oo = ndarray::Array::logspace(2_f32, -127., 127., 2_000); 292 | let mut max: f32 = 0.; 293 | for o in oo { 294 | let r = round(o, 9); 295 | let diff_percent = ((o - r) / o).abs() * 100.; 296 | if diff_percent > max { 297 | max = diff_percent; 298 | } 299 | println!("{diff_percent:.2} {o} {r}"); 300 | } 301 | assert!(max < 0.5); // less than 1/2 of 1% Grror 302 | println!("max diff % {max:.3}"); 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /src/ifs.rs: -------------------------------------------------------------------------------- 1 | // use anyhow::anyhow; 2 | // use anyhow::Result; 3 | // use chrono::{DateTime, TimeDelta, TimeZone, Utc}; 4 | // use futures::future::join_all; 5 | // use futures::stream; 6 | // use futures::{StreamExt, TryStreamExt}; 7 | // use itertools::Itertools; 8 | // use ndarray::{s, Array2, Array3, Axis}; 9 | // use object_store::aws::AmazonS3Builder; 10 | // use object_store::ObjectStore; 11 | // use regex::Regex; 12 | // use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; 13 | // use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware}; 14 | // use std::cmp::min; 15 | // use std::mem::size_of_val; 16 | // use std::sync::Arc; 17 | // use std::time::Duration; 18 | // use std::time::Instant; 19 | // use tokio::task::spawn_blocking; 20 | 21 | // type HttpClient = ClientWithMiddleware; 22 | // type ObjStore = Arc; 23 | // type ChunkIdx3 = [usize; 3]; 24 | 25 | // const S3_BUCKET_HOST: &str = "https://ecmwf-forecasts.s3.amazonaws.com"; 26 | // const DEST_ROOT_PATH: &str = "aldenks/ifs-dynamical/analysis/v0.1.0.zarr"; 27 | 28 | // type E = f32; 29 | // const DATASET_SHORT_NAME: &str = "ifs"; 30 | // const VARIABLE_PARAM_CODE: &str = "tp"; 31 | // const VARIABLE_NAME: &str = "precipitation_surface"; 32 | 33 | // #[tokio::main] 34 | // async fn main() -> Result<()> { 35 | // // console_subscriber::init(); 36 | 37 | // use std::time::Instant; 38 | // let start = Instant::now(); 39 | 40 | // let dataset_start_date = Utc.with_ymd_and_hms(2024, 3, 1, 0, 0, 0).unwrap(); 41 | // let init_time_chunk_size: TimeDelta = TimeDelta::try_days(10).unwrap(); 42 | // let init_freq: TimeDelta = TimeDelta::try_hours(12).unwrap(); 43 | 44 | // let http_client = http_client()?; 45 | // let output_store = output_store()?; 46 | 47 | // let base_buffer = 1; 48 | 49 | // let results = stream::iter(0..8) // 46 50 | // .map(|i| { 51 | // let start_date = dataset_start_date + (init_time_chunk_size * i.try_into().unwrap()); 52 | // Job { 53 | // i, 54 | // start_date, 55 | // end_date: start_date + init_time_chunk_size, 56 | // init_freq, 57 | // } 58 | // }) 59 | // .map(|job| download_and_read_time_chunk(job, http_client.clone())) 60 | // .buffer_unordered(base_buffer) 61 | // .map(|(job, time_chunk)| split_space_chunks(job, time_chunk)) 62 | // .buffer_unordered(base_buffer * 8) 63 | // .flat_map(stream::iter) 64 | // .map(|(chunk_idx, array)| spawn_blocking(move || compress_chunk(chunk_idx, array))) 65 | // .buffer_unordered(base_buffer * 8) 66 | // .map(|result| async { 67 | // let (chunk_idx, data, compression_ratio) = result.unwrap().unwrap(); 68 | // let data_len = data.len(); 69 | // upload_chunk(chunk_idx, data, output_store.clone()) 70 | // .await 71 | // .unwrap(); 72 | // (compression_ratio, data_len) 73 | // }) 74 | // .buffer_unordered(base_buffer * 2) 75 | // .collect::>() 76 | // .await; 77 | 78 | // println!("\n"); 79 | // println!("{:?} chunks, {:?} elapsed", results.len(), start.elapsed()); 80 | 81 | // Ok(()) 82 | // } 83 | 84 | // async fn upload_chunk( 85 | // chunk_idx: ChunkIdx3, 86 | // data: Vec, 87 | // store: ObjStore, 88 | // ) -> Result { 89 | // let chunk_idx_name = chunk_idx.into_iter().join("."); 90 | // let chunk_path = format!("{DEST_ROOT_PATH}/{VARIABLE_NAME}/{chunk_idx_name}"); 91 | 92 | // let bytes: object_store::PutPayload = data.into(); 93 | 94 | // let s = Instant::now(); 95 | 96 | // let mut res = Err(anyhow!("Never attempted to put object")); 97 | // for retry_i in 0..16 { 98 | // match store.put(&chunk_path.clone().into(), bytes.clone()).await { 99 | // Ok(r) => { 100 | // println!( 101 | // "Uploaded {:?} in {:.2?} ({:?} mb)", 102 | // &chunk_idx, 103 | // s.elapsed(), 104 | // bytes.content_length() / 10_usize.pow(6) 105 | // ); 106 | // return Ok(r); 107 | // } 108 | // Err(e) => { 109 | // println!( 110 | // "upload err - retry i: {:?}, chunk {:?}", 111 | // retry_i, &chunk_idx 112 | // ); 113 | // tokio::time::sleep(Duration::from_secs_f32( 114 | // 8_f32.min(2_f32.powf(retry_i as f32)), 115 | // )) 116 | // .await; 117 | // res = Err(anyhow!(e)); 118 | // } 119 | // } 120 | // } 121 | // res 122 | // } 123 | 124 | // fn compress_chunk(chunk_idx: ChunkIdx3, array: Array3) -> Result<(ChunkIdx3, Vec, f64)> { 125 | // let array_slice = array 126 | // .as_slice() 127 | // .expect("TODO handle non default memory ordering"); 128 | // let slice_byte_len = size_of_val(array_slice); 129 | // let element_size = size_of_val(&array_slice[0]); 130 | 131 | // // println!("Compressing {:?}", chunk_idx); 132 | 133 | // let context = blosc::Context::new() 134 | // .compressor(blosc::Compressor::Zstd) 135 | // .unwrap() 136 | // .typesize(Some(element_size)) 137 | // .clevel(blosc::Clevel::L5) 138 | // .shuffle(blosc::ShuffleMode::Byte); 139 | 140 | // let compressed: Vec = context.compress(array_slice).into(); 141 | 142 | // let compression_ratio = compressed.len() as f64 / slice_byte_len as f64; 143 | 144 | // Ok((chunk_idx, compressed, compression_ratio)) 145 | // } 146 | 147 | // async fn split_space_chunks(job: Job, time_chunk: Array3) -> Vec<(ChunkIdx3, Array3)> { 148 | // spawn_blocking(move || { 149 | // let [time_size, y_size, x_size] = time_chunk.shape() else { 150 | // panic!("expected 3D array") 151 | // }; 152 | 153 | // let y_chunk_size = 361; 154 | // let x_chunk_size = 360; 155 | 156 | // let y_n_chunks = ((*y_size as f64) / (y_chunk_size as f64)).ceil() as usize; 157 | // let x_n_chunks = ((*x_size as f64) / (x_chunk_size as f64)).ceil() as usize; 158 | 159 | // let chunk_i_tuples = (0..y_n_chunks).cartesian_product(0..x_n_chunks); 160 | 161 | // chunk_i_tuples 162 | // .map(|(y_chunk_i, x_chunk_i)| { 163 | // let mut chunk = Array3::from_elem([*time_size, y_chunk_size, x_chunk_size], E::NAN); 164 | 165 | // let y_start = y_chunk_i * y_chunk_size; 166 | // let y_stop = min(y_start + y_chunk_size, *y_size); 167 | // let x_start = x_chunk_i * x_chunk_size; 168 | // let x_stop = min(x_start + x_chunk_size, *x_size); 169 | 170 | // chunk 171 | // .slice_mut(s![.., ..(y_stop - y_start), ..(x_stop - x_start)]) 172 | // .assign(&time_chunk.slice(s![.., y_start..y_stop, x_start..x_stop])); 173 | 174 | // let chunk_idx = [job.i, y_chunk_i, x_chunk_i]; 175 | // // println!("Split {:?}", &chunk_idx); 176 | // (chunk_idx, chunk) 177 | // }) 178 | // .collect::>() 179 | // }) 180 | // .await 181 | // .unwrap() 182 | // } 183 | 184 | // async fn download_and_read_time_chunk(job: Job, http_client: HttpClient) -> (Job, Array3) { 185 | // let mut futures = vec![]; 186 | // let mut init_time = job.start_date; 187 | // while init_time < job.end_date { 188 | // futures.push(load_file(init_time, http_client.clone())); 189 | // init_time += job.init_freq; 190 | // } 191 | // let results = join_all(futures).await; 192 | // let arrays = results 193 | // .into_iter() 194 | // .map(|r| { 195 | // r.inspect_err(|e| eprintln!("Error getting chunk! {e:?}")) 196 | // .unwrap_or_else(|_| Array2::from_elem((721, 1440), E::NAN)) 197 | // }) 198 | // .collect::>(); 199 | // let array_views = arrays 200 | // .iter() 201 | // .map(ndarray::ArrayBase::view) 202 | // .collect::>(); 203 | // let array = ndarray::stack(Axis(0), &array_views).expect("Array shapes must be stackable"); 204 | 205 | // (job, array) 206 | // } 207 | 208 | // async fn load_file(init_time: DateTime, http_client: HttpClient) -> Result> { 209 | // let (init_date, init_hour) = (init_time.format("%Y%m%d"), init_time.format("%H")); 210 | 211 | // let base_data_path = format!( 212 | // "{init_date}/{init_hour}z/{DATASET_SHORT_NAME}/0p25/oper/{init_date}{init_hour}0000-0h-oper-fc" 213 | // ); 214 | // let data_path = format!("{base_data_path}.grib2"); 215 | 216 | // let data_url = format!("{S3_BUCKET_HOST}/{data_path}"); 217 | // let index_url = format!("{S3_BUCKET_HOST}/{base_data_path}.index"); 218 | 219 | // let index_contents = http_client 220 | // .get(&index_url) 221 | // .send() 222 | // .await? 223 | // .error_for_status()? 224 | // .text() 225 | // .await?; 226 | 227 | // let byte_offset_regex = Regex::new(&format!( 228 | // r#""levtype": "sfc".*"param": "{VARIABLE_PARAM_CODE}", "_offset": (\d*), "_length": (\d*)"# 229 | // )) 230 | // .unwrap(); 231 | 232 | // let (_, [start_offset_str, length_str]) = byte_offset_regex 233 | // .captures(&index_contents) 234 | // .ok_or(anyhow!("Couldn't parse .index {}", &index_url))? 235 | // .extract::<2>(); 236 | // let start_offset: u64 = start_offset_str.parse()?; 237 | // let length: u64 = length_str.parse()?; 238 | // // let end_offset = start_offset + length - 1; // http range is inclusive of endpoint, subtract 1 239 | // let end_offset = start_offset + length; 240 | 241 | // assert!(start_offset < end_offset); 242 | 243 | // let response = http_client 244 | // .get(&data_url) 245 | // .header("Range", format!("bytes={start_offset}-{end_offset}")) 246 | // .send() 247 | // .await?; 248 | 249 | // // Reqwest byte streams implement futures::AsyncRead, tokio's copy wants tokio::AsyncRead, this translates. 250 | // let mut data_stream = 251 | // tokio_util::io::StreamReader::new(response.bytes_stream().map_err(std::io::Error::other)); 252 | 253 | // let file_path = format!("/tmp/{DATASET_SHORT_NAME}/{data_path}"); 254 | // let file_dir = std::path::Path::new(&file_path) 255 | // .parent() 256 | // .expect("file_path must have parent dir"); 257 | // tokio::fs::create_dir_all(file_dir).await?; 258 | // let mut file = tokio::fs::File::create(&file_path).await?; 259 | 260 | // tokio::io::copy(&mut data_stream, &mut file).await?; 261 | 262 | // let array = spawn_blocking(move || -> Result> { 263 | // let dataset = gdal::Dataset::open(file_path)?; 264 | // let band = dataset.rasterband(1)?; 265 | // let array = band.read_as_array::((0, 0), band.size(), band.size(), None)?; 266 | // Ok(array) 267 | // }) 268 | // .await??; 269 | 270 | // Ok(array) 271 | // } 272 | 273 | // #[derive(Default, Debug, Copy, Clone)] 274 | // struct Job { 275 | // i: usize, 276 | // start_date: DateTime, 277 | // end_date: DateTime, 278 | // init_freq: TimeDelta, 279 | // } 280 | 281 | // fn http_client() -> Result { 282 | // let client = reqwest::Client::builder() 283 | // .timeout(Duration::from_secs(45)) 284 | // .connect_timeout(Duration::from_secs(5)) 285 | // .redirect(reqwest::redirect::Policy::none()) 286 | // .https_only(true) 287 | // .build()?; 288 | 289 | // let backoff_policy = ExponentialBackoff::builder().build_with_max_retries(32); 290 | 291 | // Ok(ClientBuilder::new(client) 292 | // .with(RetryTransientMiddleware::new_with_policy(backoff_policy)) 293 | // .build()) 294 | // } 295 | 296 | // fn output_store() -> Result { 297 | // let mut store_builder = AmazonS3Builder::from_env() 298 | // .with_access_key_id(std::env::var("OUTPUT_STORE_ACCESS_KEY_ID")?) 299 | // .with_secret_access_key(std::env::var("OUTPUT_STORE_SECRET_ACCESS_KEY")?); 300 | 301 | // if let Ok(bucket) = std::env::var("OUTPUT_STORE_BUCKET") { 302 | // store_builder = store_builder.with_bucket_name(bucket); 303 | // } 304 | // if let Ok(endpoint) = std::env::var("OUTPUT_STORE_ENDPOINT") { 305 | // store_builder = store_builder.with_endpoint(endpoint); 306 | // } 307 | // if let Ok(region) = std::env::var("OUTPUT_STORE_REGION") { 308 | // store_builder = store_builder.with_region(region); 309 | // } 310 | 311 | // let store = store_builder.build()?; 312 | // Ok(Arc::new(store)) 313 | // } 314 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod binary_round; 2 | pub mod gfs; 3 | pub mod http; 4 | pub mod output; 5 | use std::{collections::HashMap, error::Error, mem::size_of_val}; 6 | 7 | use anyhow::Result; 8 | use backon::{ExponentialBuilder, Retryable}; 9 | use chrono::{DateTime, TimeDelta, Utc}; 10 | use futures::future::join_all; 11 | use output::{PutPayload, PutResult, Storage}; 12 | use serde::{Deserialize, Serialize}; 13 | use serde_json::{json, Value}; 14 | use std::sync::Arc; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct DataVariable { 18 | pub name: &'static str, 19 | pub long_name: &'static str, 20 | pub units: &'static str, 21 | pub grib_variable_name: &'static str, 22 | pub dtype: &'static str, 23 | pub statistics_approximate: Value, 24 | } 25 | 26 | #[derive(Debug, Clone)] 27 | pub struct DataDimension { 28 | pub name: &'static str, 29 | pub long_name: &'static str, 30 | pub units: &'static str, 31 | pub dtype: &'static str, 32 | pub extra_metadata: HashMap<&'static str, &'static str>, 33 | pub statistics_approximate: Value, 34 | } 35 | 36 | #[derive(Debug, Clone)] 37 | pub struct AnalysisDataset { 38 | pub id: &'static str, 39 | pub name: &'static str, 40 | pub description: &'static str, 41 | pub url: &'static str, 42 | pub spatial_coverage: &'static str, 43 | pub spatial_resolution: &'static str, 44 | pub attribution: &'static str, 45 | 46 | pub time_start: DateTime, 47 | pub time_end: DateTime, 48 | pub time_step: TimeDelta, 49 | pub time_chunk_size: usize, 50 | 51 | pub longitude_start: f64, 52 | pub longitude_end: f64, 53 | pub longitude_step: f64, 54 | pub longitude_chunk_size: usize, 55 | 56 | pub latitude_start: f64, 57 | pub latitude_end: f64, 58 | pub latitude_step: f64, 59 | pub latitude_chunk_size: usize, 60 | 61 | pub data_dimensions: Vec, 62 | pub data_variables: Vec, 63 | } 64 | 65 | #[derive(Debug, Clone)] 66 | pub struct AnalysisRunConfig { 67 | pub dataset: AnalysisDataset, 68 | pub data_variable: DataVariable, 69 | pub time_coordinates: Arc>>, // Arc because this struct is cloned often and this vec can be big 70 | pub latitude_coordinates: Vec, 71 | pub longitude_coordinates: Vec, 72 | pub time_start: DateTime, 73 | pub time_end: DateTime, 74 | } 75 | 76 | #[allow( 77 | clippy::cast_possible_truncation, 78 | clippy::cast_sign_loss, 79 | clippy::cast_precision_loss 80 | )] 81 | fn num_chunks(size: usize, chunk_size: usize) -> usize { 82 | ((size as f64) / (chunk_size as f64)).ceil() as usize 83 | } 84 | 85 | async fn write_bytes( 86 | file_path: &str, 87 | bytes: Vec, 88 | object_store: Storage, 89 | dest_root_path: &str, 90 | ) -> Result<()> { 91 | let payload: PutPayload = bytes.into(); 92 | (|| async { 93 | do_upload( 94 | object_store.clone(), 95 | format!("{dest_root_path}/{file_path}"), 96 | payload.clone(), 97 | ) 98 | .await 99 | }) 100 | .retry(&ExponentialBuilder::default()) 101 | .await?; 102 | 103 | Ok(()) 104 | } 105 | 106 | async fn write_json( 107 | file_path: &str, 108 | json_value: &Value, 109 | object_store: Storage, 110 | dest_root_path: &str, 111 | ) -> Result<()> { 112 | let json_string = serde_json::to_string_pretty(json_value)?; 113 | write_bytes( 114 | file_path, 115 | json_string.as_bytes().to_vec(), 116 | object_store, 117 | dest_root_path, 118 | ) 119 | .await 120 | } 121 | 122 | fn to_value(value: &T) -> serde_json::Value { 123 | serde_json::to_value(value).unwrap() 124 | } 125 | 126 | #[derive(Serialize, Deserialize, Debug, Clone)] 127 | struct ZarrCompressorMetadata { 128 | blocksize: usize, 129 | clevel: usize, 130 | cname: &'static str, 131 | id: &'static str, 132 | shuffle: usize, 133 | } 134 | 135 | #[derive(Serialize, Deserialize, Debug, Clone)] 136 | struct ZarrFilterMetadata {} 137 | 138 | #[derive(Serialize, Deserialize, Debug, Clone)] 139 | struct ZarrArrayMetadata { 140 | chunks: Vec, 141 | compressor: Option, 142 | dtype: &'static str, 143 | fill_value: Option<&'static str>, 144 | filters: Option>, 145 | order: &'static str, 146 | shape: Vec, 147 | zarr_format: usize, 148 | } 149 | 150 | #[allow(non_snake_case)] 151 | #[derive(Serialize, Deserialize, Debug, Clone)] 152 | struct ZarrVariableAttributeMetadata { 153 | _ARRAY_DIMENSIONS: Vec<&'static str>, 154 | long_name: &'static str, 155 | units: &'static str, 156 | grid_mapping: &'static str, 157 | statistics_approximate: Value, 158 | } 159 | 160 | #[allow(non_snake_case)] 161 | #[derive(Serialize, Deserialize, Debug, Clone)] 162 | struct ZarrDimensionAttributeMetadata { 163 | _ARRAY_DIMENSIONS: Vec<&'static str>, 164 | long_name: &'static str, 165 | units: &'static str, 166 | statistics_approximate: Value, 167 | } 168 | 169 | async fn write_array_metadata( 170 | field_name: &str, 171 | mut zmetadata: HashMap, 172 | zarr_array_json: &serde_json::Value, 173 | zarr_attribute_json: &serde_json::Value, 174 | store: Storage, 175 | dest_root_path: &str, 176 | ) -> Result> { 177 | zmetadata.insert(format!("{field_name}/.zarray"), to_value(&zarr_array_json)); 178 | zmetadata.insert( 179 | format!("{field_name}/.zattrs"), 180 | to_value(&zarr_attribute_json), 181 | ); 182 | 183 | write_json( 184 | &format!("{field_name}/.zarray"), 185 | zarr_array_json, 186 | store.clone(), 187 | dest_root_path, 188 | ) 189 | .await?; 190 | write_json( 191 | &format!("{field_name}/.zattrs"), 192 | zarr_attribute_json, 193 | store.clone(), 194 | dest_root_path, 195 | ) 196 | .await?; 197 | 198 | Ok(zmetadata.clone()) 199 | } 200 | 201 | impl AnalysisRunConfig { 202 | #[allow(clippy::too_many_lines)] 203 | #[allow(clippy::cast_possible_truncation)] 204 | #[allow(clippy::cast_sign_loss)] 205 | pub async fn write_zarr_metadata(&self, store: Storage, dest_root_path: &str) -> Result<()> { 206 | let data_variable_compressor_metadata = ZarrCompressorMetadata { 207 | blocksize: 0, 208 | clevel: 5, 209 | cname: "zstd", 210 | id: "blosc", 211 | shuffle: 1, 212 | }; 213 | let order = "C"; 214 | let fill_value = "NaN"; 215 | let zarr_format = 2; 216 | 217 | let zgroup = json!({"zarr_format": 2}); 218 | 219 | let mut zmetadata: HashMap = HashMap::new(); 220 | 221 | let time_shape_size = self.time_coordinates.len(); 222 | let latitude_shape_size = self.latitude_coordinates.len(); 223 | let longitude_shape_size = self.longitude_coordinates.len(); 224 | 225 | // Data variable metadata 226 | for data_variable in self.dataset.data_variables.clone() { 227 | let zarr_array_metadata = ZarrArrayMetadata { 228 | chunks: [ 229 | self.dataset.time_chunk_size, 230 | self.dataset.latitude_chunk_size, 231 | self.dataset.longitude_chunk_size, 232 | ] 233 | .to_vec(), 234 | compressor: Some(data_variable_compressor_metadata.clone()), 235 | dtype: data_variable.dtype, 236 | fill_value: Some(fill_value), 237 | filters: None, 238 | order, 239 | shape: [time_shape_size, latitude_shape_size, longitude_shape_size].to_vec(), 240 | zarr_format, 241 | }; 242 | 243 | let zarr_attribute_metadata = ZarrVariableAttributeMetadata { 244 | _ARRAY_DIMENSIONS: ["time", "latitude", "longitude"].to_vec(), 245 | long_name: data_variable.long_name, 246 | units: data_variable.units, 247 | grid_mapping: "spatial_ref", 248 | statistics_approximate: data_variable.statistics_approximate, 249 | }; 250 | 251 | zmetadata = write_array_metadata( 252 | data_variable.name, 253 | zmetadata.clone(), 254 | &to_value(&zarr_array_metadata), 255 | &to_value(&zarr_attribute_metadata), 256 | store.clone(), 257 | dest_root_path, 258 | ) 259 | .await?; 260 | } 261 | 262 | let data_dimension_compressor_metadata = ZarrCompressorMetadata { 263 | blocksize: 0, 264 | clevel: 5, 265 | cname: "zstd", 266 | id: "blosc", 267 | shuffle: 1, 268 | }; 269 | 270 | for data_dimension in self.dataset.data_dimensions.clone() { 271 | let shape_size = match data_dimension.name { 272 | "time" => time_shape_size, 273 | "latitude" => latitude_shape_size, 274 | "longitude" => longitude_shape_size, 275 | &_ => todo!(), 276 | }; 277 | 278 | let fill_value = match data_dimension.name { 279 | "time" => None, 280 | &_ => Some("NaN"), 281 | }; 282 | 283 | let zarr_array_metadata = ZarrArrayMetadata { 284 | chunks: [shape_size].to_vec(), 285 | compressor: Some(data_dimension_compressor_metadata.clone()), 286 | dtype: data_dimension.dtype, 287 | fill_value, 288 | filters: None, 289 | order, 290 | shape: [shape_size].to_vec(), 291 | zarr_format, 292 | }; 293 | 294 | let zarr_attribute_metadata = ZarrDimensionAttributeMetadata { 295 | _ARRAY_DIMENSIONS: [data_dimension.name].to_vec(), 296 | long_name: data_dimension.long_name, 297 | units: data_dimension.units, 298 | statistics_approximate: data_dimension.statistics_approximate, 299 | }; 300 | 301 | let mut zarr_attribute_metadata_value = to_value(&zarr_attribute_metadata); 302 | let zarr_attribute_metadata_with_extra_fields = 303 | zarr_attribute_metadata_value.as_object_mut().unwrap(); 304 | for (key, value) in data_dimension.extra_metadata { 305 | zarr_attribute_metadata_with_extra_fields.insert(key.to_string(), json!(value)); 306 | } 307 | 308 | zmetadata = write_array_metadata( 309 | data_dimension.name, 310 | zmetadata.clone(), 311 | &to_value(&zarr_array_metadata), 312 | &to_value(&zarr_attribute_metadata_with_extra_fields), 313 | store.clone(), 314 | dest_root_path, 315 | ) 316 | .await?; 317 | } 318 | 319 | // For rioxarray support 320 | let spatial_ref_array_metadata = ZarrArrayMetadata { 321 | chunks: [].to_vec(), 322 | compressor: None, 323 | dtype: " Result<()> { 388 | let time_values: Vec = self 389 | .time_coordinates 390 | .iter() 391 | .map(DateTime::timestamp) 392 | .collect(); 393 | 394 | let time_coords_future = write_bytes( 395 | "time/0", 396 | blosc_compress(&time_values), 397 | store.clone(), 398 | dest_root_path, 399 | ); 400 | 401 | let latitude_coords_future = write_bytes( 402 | "latitude/0", 403 | blosc_compress(&self.latitude_coordinates), 404 | store.clone(), 405 | dest_root_path, 406 | ); 407 | 408 | let longitude_coords_future = write_bytes( 409 | "longitude/0", 410 | blosc_compress(&self.longitude_coordinates), 411 | store.clone(), 412 | dest_root_path, 413 | ); 414 | 415 | // rioxarray wants a coordinate with a single value called spatial_ref. 416 | // All of the coordinate reference system information is stored as attributes on this array 417 | let spatial_ref_coords_future = 418 | write_bytes("spatial_ref/0", [0].to_vec(), store.clone(), dest_root_path); 419 | 420 | join_all([ 421 | time_coords_future, 422 | latitude_coords_future, 423 | longitude_coords_future, 424 | spatial_ref_coords_future, 425 | ]) 426 | .await; 427 | 428 | Ok(()) 429 | } 430 | } 431 | 432 | pub async fn do_upload(store: Storage, chunk_path: String, bytes: PutPayload) -> Result { 433 | let mut put_result_result = store.put(&chunk_path.into(), bytes).await; 434 | 435 | // A little nonsense to ignore deeply nested error if the ETag isn't 436 | // present on the response. The put still succeeds in this case. 437 | if let Err(e) = &put_result_result { 438 | if let Some(source) = e.source() { 439 | if let Some(source) = source.source() { 440 | if source.to_string().starts_with("ETag Header missing") { 441 | put_result_result = Ok(PutResult { 442 | e_tag: None, 443 | version: None, 444 | }); 445 | } 446 | } 447 | } 448 | } 449 | 450 | Ok(put_result_result?) 451 | } 452 | 453 | pub fn blosc_compress(data: &[T]) -> Vec { 454 | let element_size = size_of_val(&data[0]); 455 | 456 | let context = blosc::Context::new() 457 | .compressor(blosc::Compressor::Zstd) 458 | .unwrap() 459 | .typesize(Some(element_size)) 460 | .clevel(blosc::Clevel::L5) 461 | .shuffle(blosc::ShuffleMode::Byte); 462 | 463 | context.compress(data).into() 464 | } 465 | -------------------------------------------------------------------------------- /src/gfs.rs: -------------------------------------------------------------------------------- 1 | use std::mem::size_of_val; 2 | use std::sync::Arc; 3 | use std::time::{Duration, Instant}; 4 | use std::{cmp::min, collections::HashMap}; 5 | 6 | use crate::do_upload; 7 | use crate::http; 8 | use crate::num_chunks; 9 | use crate::AnalysisDataset; 10 | use crate::AnalysisRunConfig; 11 | use crate::DataDimension; 12 | use crate::DataVariable; 13 | use crate::{binary_round, output}; 14 | pub use anyhow::{anyhow, Result}; 15 | use backon::ExponentialBuilder; 16 | use backon::Retryable; 17 | use chrono::{DateTime, TimeDelta, TimeZone, Timelike, Utc}; 18 | use futures::future::join_all; 19 | use futures::stream::StreamExt; 20 | use futures::TryStreamExt; 21 | use itertools::Itertools; 22 | use ndarray::{s, Array1, Array2, Array3, Axis}; 23 | use once_cell::sync::Lazy; 24 | use output::Storage; 25 | use regex::Regex; 26 | use serde_json::json; 27 | use tokio::task::spawn_blocking; 28 | 29 | const S3_BUCKET_HOST: &str = "https://noaa-gfs-bdp-pds.s3.amazonaws.com"; 30 | 31 | // if using dynamical.s3.source.coop endpoint 32 | const DEST_ROOT_PATH: &str = "analysis-hourly/v0.1.0.zarr"; 33 | // if using standard s3 source coop access with session token 34 | // const DEST_ROOT_PATH: &str = "dynamical/gfs/analysis-hourly/v0.1.0.zarr"; 35 | 36 | /// Dataset config object todos 37 | /// - incorporate element type into object 38 | /// - make dimension objects which contain names, etc 39 | /// - make data variable enum which contains name, dimensions, chunking, units, grib index names, etc 40 | type E = f32; // element type 41 | 42 | static HOURLY_AWS_FORECAST_START: Lazy> = 43 | Lazy::new(|| Utc.with_ymd_and_hms(2021, 2, 27, 0, 0, 0).unwrap()); 44 | const INIT_FREQUENCY_HOURS: i64 = 6; 45 | const EARLY_DATA_FREQUENCY_HOURS: u32 = 3; 46 | 47 | /// # Errors 48 | /// 49 | /// Currently never returns an error, but it should report errors 50 | pub async fn reformat( 51 | data_variable_name: String, 52 | ingest_start: DateTime, 53 | ingest_end: DateTime, 54 | dataset_end: DateTime, 55 | destination: String, 56 | skip_metadata: bool, 57 | ) -> Result<()> { 58 | let start = Instant::now(); 59 | 60 | let gfs_dataset = get_dataset(dataset_end); 61 | 62 | let run_config = get_run_config(&gfs_dataset, &data_variable_name, ingest_start, ingest_end)?; 63 | 64 | let download_batches = run_config.get_download_batches()?; 65 | 66 | let http_client = http::client()?; 67 | 68 | let object_store = output::get_object_store(&destination)?; 69 | 70 | if !skip_metadata { 71 | run_config 72 | .write_zarr_metadata(object_store.clone(), DEST_ROOT_PATH) 73 | .await?; 74 | run_config 75 | .write_dimension_coordinates(object_store.clone(), DEST_ROOT_PATH) 76 | .await?; 77 | } 78 | 79 | let results = futures::stream::iter(download_batches) 80 | .map(|download_batch| download_batch.process(http_client.clone())) 81 | .buffer_unordered(2) 82 | .map(DownloadedBatch::zarr_array_chunks) 83 | .buffer_unordered(30) 84 | .flat_map(futures::stream::iter) 85 | .map(ZarrChunkArray::compress) 86 | .buffer_unordered(30) 87 | .map(|zarr_chunk_compressed| zarr_chunk_compressed.upload(object_store.clone())) 88 | .buffer_unordered(30) 89 | .collect::>() 90 | .await; 91 | 92 | print_report(results, start.elapsed()); 93 | 94 | Ok(()) 95 | } 96 | 97 | #[derive(Debug, Clone)] 98 | pub struct DownloadBatch { 99 | run_config: AnalysisRunConfig, 100 | data_variable_name: String, 101 | time_coordinates: Vec>, 102 | time_chunk_index: usize, 103 | } 104 | 105 | #[derive(Debug, Clone)] 106 | pub struct DownloadedBatch { 107 | run_config: AnalysisRunConfig, 108 | data_variable_name: String, 109 | time_chunk_index: usize, 110 | array: Array3, 111 | } 112 | 113 | #[derive(Debug, Clone)] 114 | pub struct ZarrChunkArray { 115 | run_config: AnalysisRunConfig, 116 | data_variable_name: String, 117 | time_chunk_index: usize, 118 | longitude_chunk_index: usize, 119 | latitude_chunk_index: usize, 120 | array: Array3, 121 | } 122 | 123 | #[derive(Debug, Clone)] 124 | pub struct ZarrChunkCompressed { 125 | run_config: AnalysisRunConfig, 126 | data_variable_name: String, 127 | time_chunk_index: usize, 128 | longitude_chunk_index: usize, 129 | latitude_chunk_index: usize, 130 | bytes: Vec, 131 | uncompressed_length: usize, 132 | compressed_length: usize, 133 | } 134 | 135 | #[allow(dead_code)] // We report but don't currently do something with all these values 136 | #[derive(Debug, Clone)] 137 | pub struct ZarrChunkUploadInfo { 138 | run_config: AnalysisRunConfig, 139 | data_variable_name: String, 140 | time_chunk_index: usize, 141 | longitude_chunk_index: usize, 142 | latitude_chunk_index: usize, 143 | uncompressed_mb: f64, 144 | compressed_mb: f64, 145 | upload_time: Duration, 146 | e_tag: Option, 147 | object_version: Option, 148 | } 149 | 150 | #[allow(clippy::too_many_lines)] 151 | fn get_dataset(dataset_max_date: DateTime) -> AnalysisDataset { 152 | let dataset_min_date = Utc.with_ymd_and_hms(2015, 1, 15, 0, 0, 0).unwrap(); 153 | let dataset_mean_date = dataset_min_date + ((dataset_max_date - dataset_min_date) / 2); 154 | AnalysisDataset { 155 | id: "noaa-gfs-analysis-hourly", 156 | name: "NOAA GFS analysis, hourly", 157 | description: 158 | "Historical weather data from the Global Forecast System (GFS) model operated by NOAA NCEP.", 159 | url: "https://data.dynamical.org/noaa/gfs/analysis-hourly/latest.zarr", 160 | spatial_coverage: "Global", 161 | spatial_resolution: "0.25 degrees (~20km)", 162 | attribution: 163 | "NOAA NCEP GFS data processed by dynamical.org from NCAR and NOAA BDP AWS archives.", 164 | 165 | time_start: dataset_min_date, 166 | time_end: dataset_max_date, 167 | time_step: TimeDelta::try_hours(1).unwrap(), 168 | time_chunk_size: 160, 169 | 170 | longitude_start: -180., 171 | longitude_end: 180., 172 | longitude_step: 0.25, 173 | longitude_chunk_size: 144, 174 | 175 | latitude_start: 90., 176 | latitude_end: -90., 177 | latitude_step: 0.25, 178 | latitude_chunk_size: 145, 179 | 180 | data_dimensions: vec![ 181 | DataDimension { 182 | name: "time", 183 | long_name: "Time", 184 | units: "seconds since 1970-01-01 00:00:00", 185 | dtype: ", 275 | time_end: DateTime, 276 | ) -> Result { 277 | let data_variable = dataset 278 | .data_variables 279 | .iter() 280 | .find(|data_variable| data_variable.name == data_variable_name); 281 | 282 | if data_variable.is_none() { 283 | return Err(anyhow!("Variable name {data_variable_name} not supported.")); 284 | } 285 | 286 | if time_start < dataset.time_start { 287 | return Err(anyhow!( 288 | "Start time {time_start} is before dataset time {}", 289 | dataset.time_start 290 | )); 291 | } 292 | 293 | if dataset.time_end < time_end { 294 | return Err(anyhow!( 295 | "End time {time_end} is after dataset time {}", 296 | dataset.time_end 297 | )); 298 | } 299 | 300 | let time_coordinates = (0..u32::MAX) 301 | .scan(dataset.time_start - dataset.time_step, |time, _| { 302 | *time += dataset.time_step; 303 | if *time <= dataset.time_end { 304 | Some(*time) 305 | } else { 306 | None 307 | } 308 | }) 309 | .collect::>(); 310 | 311 | let latitude_coordinates = (0..u32::MAX) 312 | .scan( 313 | dataset.latitude_start + dataset.latitude_step, 314 | |latitude, _| { 315 | *latitude -= dataset.latitude_step; 316 | if *latitude >= dataset.latitude_end { 317 | Some(*latitude) 318 | } else { 319 | None 320 | } 321 | }, 322 | ) 323 | .collect::>(); 324 | 325 | let longitude_coordinates = (0..u32::MAX) 326 | .scan( 327 | dataset.longitude_start - dataset.longitude_step, 328 | |longitude, _| { 329 | *longitude += dataset.longitude_step; 330 | if *longitude < dataset.longitude_end { 331 | Some(*longitude) 332 | } else { 333 | None 334 | } 335 | }, 336 | ) 337 | .collect::>(); 338 | 339 | Ok(AnalysisRunConfig { 340 | dataset: dataset.clone(), 341 | data_variable: data_variable.unwrap().clone(), 342 | time_coordinates: Arc::new(time_coordinates), 343 | latitude_coordinates, 344 | longitude_coordinates, 345 | time_start, 346 | time_end, 347 | }) 348 | } 349 | 350 | impl AnalysisRunConfig { 351 | /// # Errors 352 | /// 353 | /// Returns `Err` if `self.time_coordinates` is empty. 354 | pub fn get_download_batches(&self) -> Result> { 355 | if self.time_coordinates.is_empty() { 356 | return Err(anyhow!("Can't make batches from no timesteps")); 357 | } 358 | Ok(self 359 | .time_coordinates 360 | .iter() 361 | .chunks(self.dataset.time_chunk_size) 362 | .into_iter() 363 | .enumerate() 364 | .map(|(time_chunk_index, chunk_time_coordinates)| DownloadBatch { 365 | run_config: self.clone(), 366 | data_variable_name: self.data_variable.name.to_string(), 367 | time_coordinates: chunk_time_coordinates.copied().collect(), 368 | time_chunk_index, 369 | }) 370 | .filter(|download_batch| { 371 | let download_batch_start_date = download_batch.time_coordinates.first().unwrap(); 372 | let download_batch_end_date = download_batch.time_coordinates.last().unwrap(); 373 | // If the download_batch_start_date is between the run_config's start and end date 374 | // or the download_batch_end_date is between the run_config's start and end date 375 | // then this is a download batch that we want to process 376 | (download_batch_start_date >= &self.time_start 377 | && download_batch_start_date <= &self.time_end) 378 | || (download_batch_end_date >= &self.time_start 379 | && download_batch_end_date <= &self.time_end) 380 | }) 381 | .collect()) 382 | } 383 | } 384 | 385 | impl DownloadBatch { 386 | async fn process(self, http_client: http::Client) -> DownloadedBatch { 387 | println!( 388 | "Starting to process {} to {}", 389 | self.time_coordinates.first().unwrap(), 390 | self.time_coordinates.last().unwrap() 391 | ); 392 | 393 | let (needs_interpolation, time_coordinates_to_download) = 394 | self.time_coordinates_to_download(); 395 | 396 | // filter time coords to 3 hourly 397 | // load with filtered time coordinates 398 | // x = filtered time coords (transformed into seconds since x) 399 | // q = self.time_coordinates (transformed into seconds since x) 400 | // 401 | 402 | let load_file_futures = 403 | time_coordinates_to_download 404 | .clone() 405 | .into_iter() 406 | .map(|time_coordinate| { 407 | load_variable_from_file( 408 | &self.run_config.data_variable, 409 | time_coordinate, 410 | http_client.clone(), 411 | ) 412 | }); 413 | 414 | let results = join_all(load_file_futures).await; 415 | 416 | let arrays = results 417 | .into_iter() 418 | .map(|r| { 419 | r.inspect_err(|e| { 420 | eprintln!( 421 | "Error getting chunk {} - {}! {e:?}", 422 | self.time_coordinates.first().unwrap(), 423 | self.time_coordinates.last().unwrap() 424 | ); 425 | }) 426 | .unwrap_or_else(|_| Array2::from_elem((721, 1440), E::NAN)) 427 | }) 428 | .collect::>(); 429 | 430 | let array = spawn_blocking(move || { 431 | let array_views = arrays 432 | .iter() 433 | .map(ndarray::ArrayBase::view) 434 | .collect::>(); 435 | 436 | let mut array = 437 | ndarray::stack(Axis(0), &array_views).expect("Array shapes must be stackable"); 438 | 439 | if needs_interpolation { 440 | let interpolator = ndarray_interp::interp1d::Interp1DBuilder::new(array) 441 | .x(to_timestamps(&time_coordinates_to_download)) 442 | .strategy(ndarray_interp::interp1d::Linear::new()) 443 | .build() 444 | .unwrap(); 445 | 446 | array = interpolator 447 | .interp_array(&to_timestamps(&self.time_coordinates)) 448 | .unwrap(); 449 | } 450 | 451 | // improve compression ratio by rounding to keep n mantissa bits, setting the rest to zeros 452 | array.mapv_inplace(|v| binary_round::round(v, 9)); 453 | 454 | array 455 | }) 456 | .await 457 | .unwrap(); 458 | 459 | DownloadedBatch { 460 | array, 461 | run_config: self.run_config.clone(), 462 | data_variable_name: self.data_variable_name.clone(), 463 | time_chunk_index: self.time_chunk_index, 464 | } 465 | } 466 | 467 | /// Add `EARLY_DATA_FREQUENCY - 1` time coordinates to the start and end of `self.time_coordinates` 468 | fn time_coordinates_to_download(&self) -> (bool, Vec>) { 469 | let first = self.time_coordinates.first().unwrap(); 470 | let last = self.time_coordinates.last().unwrap(); 471 | let time_step_hours: usize = self 472 | .run_config 473 | .dataset 474 | .time_step 475 | .num_hours() 476 | .try_into() 477 | .unwrap(); 478 | 479 | if first >= &HOURLY_AWS_FORECAST_START { 480 | let needs_interpolation = false; 481 | return (needs_interpolation, self.time_coordinates.clone()); 482 | } 483 | 484 | let mut padded = self.time_coordinates.clone(); 485 | 486 | for hour in (1..EARLY_DATA_FREQUENCY_HOURS).step_by(time_step_hours) { 487 | let coord = *first - TimeDelta::try_hours(hour.into()).unwrap(); 488 | padded.insert(0, coord); 489 | } 490 | 491 | for hour in (1..EARLY_DATA_FREQUENCY_HOURS).step_by(time_step_hours) { 492 | let coord = *last + TimeDelta::try_hours(hour.into()).unwrap(); 493 | padded.push(coord); 494 | } 495 | 496 | let filtered_time_coordinates = padded 497 | .into_iter() 498 | .filter(|t| { 499 | t > &HOURLY_AWS_FORECAST_START || t.hour() % EARLY_DATA_FREQUENCY_HOURS == 0 500 | }) 501 | .collect_vec(); 502 | 503 | let needs_interpolation = true; 504 | (needs_interpolation, filtered_time_coordinates) 505 | } 506 | } 507 | 508 | fn to_timestamps(times: &[DateTime]) -> Array1 { 509 | #[allow(clippy::cast_precision_loss)] 510 | Array1::from_iter(times.iter().map(|t| t.timestamp() as f32)) 511 | } 512 | 513 | async fn load_variable_from_file( 514 | data_variable: &DataVariable, 515 | time_coordinate: DateTime, 516 | http_client: http::Client, 517 | ) -> Result> { 518 | let file_path = if time_coordinate < *HOURLY_AWS_FORECAST_START { 519 | // Data is 3 hourly in this period 520 | if time_coordinate.hour() % EARLY_DATA_FREQUENCY_HOURS != 0 { 521 | return Ok(Array2::from_elem((721, 1440), E::NAN)); 522 | } 523 | let local_data_dir = std::env::var("LOCAL_DATA_DIR")?; 524 | // let date_str = time_coordinate.format("%Y%m%d"); 525 | // let lead_time_hour = i64::from(time_coordinate.hour()) % INIT_FREQUENCY_HOURS; 526 | let lead_time_hour = i64::from(time_coordinate.hour()) % INIT_FREQUENCY_HOURS; 527 | assert!(lead_time_hour >= 0); 528 | assert!(lead_time_hour < 6); 529 | 530 | let init_time = time_coordinate 531 | - TimeDelta::try_hours(lead_time_hour).expect("lead time hours to be within 0 - 5"); 532 | 533 | let init_time_str = init_time.format("%Y%m%d%H"); 534 | format!( 535 | "{local_data_dir}/{}/gfs.0p25.{init_time_str}.f{lead_time_hour:0>3}.grib2", data_variable.name 536 | ) 537 | } else { 538 | download_band(data_variable, time_coordinate, http_client).await? 539 | }; 540 | 541 | let file_path_move = file_path.clone(); 542 | let array = spawn_blocking(move || -> Result> { 543 | let dataset = gdal::Dataset::open(file_path_move)?; 544 | let band = dataset.rasterband(1)?; 545 | let array = band.read_as_array::((0, 0), band.size(), band.size(), None)?; 546 | Ok(array) 547 | }) 548 | .await??; 549 | 550 | if file_path.starts_with("/tmp/") { 551 | tokio::fs::remove_file(file_path).await?; 552 | } 553 | 554 | Ok(array) 555 | } 556 | 557 | async fn download_band( 558 | data_variable: &DataVariable, 559 | time_coordinate: DateTime, 560 | http_client: http::Client, 561 | ) -> Result { 562 | let lead_time_hour = i64::from(time_coordinate.hour()) % INIT_FREQUENCY_HOURS; 563 | assert!(lead_time_hour >= 0); 564 | assert!(lead_time_hour < 6); 565 | 566 | let init_time = time_coordinate 567 | - TimeDelta::try_hours(lead_time_hour).expect("lead time hours to be within 0 - 5"); 568 | 569 | let (init_date, init_hour) = (init_time.format("%Y%m%d"), init_time.format("%H")); 570 | 571 | // `atmos` and `wave` directories were added to the path starting 2021-03-23T00Z 572 | let data_path = if init_time < Utc.with_ymd_and_hms(2021, 3, 23, 0, 0, 0).unwrap() { 573 | format!("gfs.{init_date}/{init_hour}/gfs.t{init_hour}z.pgrb2.0p25.f{lead_time_hour:0>3}") 574 | } else { 575 | format!( 576 | "gfs.{init_date}/{init_hour}/atmos/gfs.t{init_hour}z.pgrb2.0p25.f{lead_time_hour:0>3}" 577 | ) 578 | }; 579 | 580 | let data_url = format!("{S3_BUCKET_HOST}/{data_path}"); 581 | let index_url = format!("{data_url}.idx"); 582 | 583 | let index_contents = http_client 584 | .get(&index_url) 585 | .send() 586 | .await? 587 | .error_for_status()? 588 | .text() 589 | .await?; 590 | 591 | let index_variable_str = data_variable.grib_variable_name; 592 | let byte_offset_regex = Regex::new(&format!( 593 | r"\d+:(\d+):.+:{index_variable_str}:.+:\n\d+:(\d+)" 594 | )) 595 | .unwrap(); 596 | 597 | let (_, [start_offset_str, end_offset_str]) = byte_offset_regex 598 | .captures(&index_contents) 599 | .ok_or(anyhow!("Couldn't parse .idx {}", &index_url))? 600 | .extract::<2>(); 601 | let start_offset: u64 = start_offset_str.parse()?; 602 | let end_offset: u64 = end_offset_str.parse()?; 603 | 604 | assert!(start_offset < end_offset); 605 | 606 | let response = http_client 607 | .get(data_url) 608 | .header("Range", format!("bytes={start_offset}-{end_offset}")) 609 | .send() 610 | .await?; 611 | 612 | let _content_length = response 613 | .content_length() 614 | .ok_or(anyhow!("s3 response missing content length"))?; 615 | 616 | // TODO its silly to write to disk just to read the whole thing back into memory right away, 617 | // figure out correct gdal memory file usage. 618 | let file_path = format!("/tmp/gfs/{data_path}"); 619 | let file_dir = std::path::Path::new(&file_path) 620 | .parent() 621 | .expect("file_path must have parent dir"); 622 | tokio::fs::create_dir_all(file_dir).await?; 623 | let mut file = tokio::fs::File::create(&file_path).await?; 624 | 625 | // Reqwest byte streams implement futures::AsyncRead, tokio's copy wants tokio::AsyncRead, this translates. 626 | let mut data_stream = 627 | tokio_util::io::StreamReader::new(response.bytes_stream().map_err(std::io::Error::other)); 628 | 629 | tokio::io::copy(&mut data_stream, &mut file).await?; 630 | 631 | Ok(file_path) 632 | } 633 | 634 | impl DownloadedBatch { 635 | async fn zarr_array_chunks(self) -> Vec { 636 | spawn_blocking(move || { 637 | let [time_size, lat_size, lon_size] = self.array.shape() else { 638 | panic!("expected 3D array") 639 | }; 640 | 641 | let lat_chunk_size = self.run_config.dataset.latitude_chunk_size; 642 | let lon_chunk_size = self.run_config.dataset.longitude_chunk_size; 643 | let time_chunk_size = self.run_config.dataset.time_chunk_size; 644 | 645 | let chunk_i_tuples = (0..num_chunks(*lat_size, lat_chunk_size)) 646 | .cartesian_product(0..num_chunks(*lon_size, lon_chunk_size)); 647 | 648 | chunk_i_tuples 649 | .map(|(lat_chunk_i, lon_chunk_i)| { 650 | // create an array the size of a full chunk and fill with nan 651 | let mut chunk = Array3::from_elem( 652 | [time_chunk_size, lat_chunk_size, lon_chunk_size], 653 | E::NAN, 654 | ); 655 | 656 | let lat_start = lat_chunk_i * lat_chunk_size; 657 | let lat_stop = min(lat_start + lat_chunk_size, *lat_size); 658 | let lon_start = lon_chunk_i * lon_chunk_size; 659 | let lon_stop = min(lon_start + lon_chunk_size, *lon_size); 660 | 661 | // write available data into the correct portion of the chunk array 662 | chunk 663 | .slice_mut(s![ 664 | ..*time_size, 665 | ..(lat_stop - lat_start), 666 | ..(lon_stop - lon_start) 667 | ]) 668 | .assign(&self.array.slice(s![ 669 | .., 670 | lat_start..lat_stop, 671 | lon_start..lon_stop 672 | ])); 673 | 674 | ZarrChunkArray { 675 | run_config: self.run_config.clone(), 676 | data_variable_name: self.data_variable_name.clone(), 677 | time_chunk_index: self.time_chunk_index, 678 | longitude_chunk_index: lon_chunk_i, 679 | latitude_chunk_index: lat_chunk_i, 680 | array: chunk, 681 | } 682 | }) 683 | .collect_vec() 684 | }) 685 | .await 686 | .unwrap() 687 | } 688 | } 689 | 690 | impl ZarrChunkArray { 691 | async fn compress(self) -> ZarrChunkCompressed { 692 | spawn_blocking(move || { 693 | let array_slice = self 694 | .array 695 | .as_slice() 696 | .expect("TODO handle non default memory ordering"); 697 | let uncompressed_length = size_of_val(array_slice); 698 | let element_size = size_of_val(&array_slice[0]); 699 | 700 | let context = blosc::Context::new() 701 | .compressor(blosc::Compressor::Zstd) 702 | .unwrap() 703 | .typesize(Some(element_size)) 704 | .clevel(blosc::Clevel::L5) 705 | .shuffle(blosc::ShuffleMode::Byte); 706 | 707 | let compressed: Vec = context.compress(array_slice).into(); 708 | let compressed_length = compressed.len(); 709 | 710 | ZarrChunkCompressed { 711 | run_config: self.run_config, 712 | data_variable_name: self.data_variable_name, 713 | time_chunk_index: self.time_chunk_index, 714 | longitude_chunk_index: self.longitude_chunk_index, 715 | latitude_chunk_index: self.latitude_chunk_index, 716 | bytes: compressed, 717 | uncompressed_length, 718 | compressed_length, 719 | } 720 | }) 721 | .await 722 | .expect("Could not compress chunk") 723 | } 724 | } 725 | 726 | impl ZarrChunkCompressed { 727 | async fn upload(self, store: Storage) -> Result { 728 | let upload_start_time = Instant::now(); 729 | 730 | let data_dimension_names: Vec<&str> = self 731 | .run_config 732 | .dataset 733 | .data_dimensions 734 | .iter() 735 | .map(|data_dimension| data_dimension.name) 736 | .collect(); 737 | assert!(data_dimension_names == vec!["time", "latitude", "longitude"]); 738 | let chunk_index_name = format!( 739 | "{}.{}.{}", 740 | self.time_chunk_index, self.latitude_chunk_index, self.longitude_chunk_index 741 | ); 742 | let chunk_path = format!( 743 | "{DEST_ROOT_PATH}/{}/{chunk_index_name}", 744 | self.data_variable_name, 745 | ); 746 | 747 | let bytes: object_store::PutPayload = self.bytes.into(); 748 | 749 | let put_result = 750 | (|| async { do_upload(store.clone(), chunk_path.clone(), bytes.clone()).await }) 751 | .retry(&ExponentialBuilder::default()) 752 | .await 753 | .inspect_err(|e| println!("Upload error, chunk {chunk_index_name}, {e}"))?; 754 | 755 | let upload_time = upload_start_time.elapsed(); 756 | 757 | println!( 758 | "Uploaded {chunk_index_name} in {upload_time:.2?} ({:?} mb)", 759 | bytes.content_length() / 10_usize.pow(6) 760 | ); 761 | 762 | #[allow(clippy::cast_precision_loss)] 763 | Ok(ZarrChunkUploadInfo { 764 | run_config: self.run_config, 765 | data_variable_name: self.data_variable_name, 766 | time_chunk_index: self.time_chunk_index, 767 | longitude_chunk_index: self.longitude_chunk_index, 768 | latitude_chunk_index: self.latitude_chunk_index, 769 | uncompressed_mb: self.uncompressed_length as f64 / 10_f64.powf(6_f64), 770 | compressed_mb: self.compressed_length as f64 / 10_f64.powf(6_f64), 771 | upload_time, 772 | e_tag: put_result.e_tag, 773 | object_version: put_result.version, 774 | }) 775 | } 776 | } 777 | 778 | #[allow(clippy::cast_precision_loss)] 779 | fn print_report(results: Vec>, elapsed: Duration) { 780 | let num_chunks = results.len(); 781 | 782 | let (uncompressed_mbs, compressed_mbs): (Vec, Vec) = results 783 | .into_iter() 784 | .filter_map(Result::ok) 785 | .map(|info| (info.uncompressed_mb, info.compressed_mb)) 786 | .collect(); 787 | 788 | let uncompressed_total_mb = uncompressed_mbs.iter().sum::(); 789 | let compressed_total_mb = compressed_mbs.iter().sum::(); 790 | let avg_compression_ratio = compressed_total_mb / uncompressed_total_mb; 791 | 792 | let avg_uncompressed_chunk_mb = uncompressed_total_mb / num_chunks as f64; 793 | let avg_compressed_chunk_mb = compressed_total_mb / num_chunks as f64; 794 | 795 | println!("\nTotals"); 796 | println!( 797 | "{:.2} GB uncompressed, {:.2} GB compressed", 798 | uncompressed_total_mb / 1e3, 799 | compressed_total_mb / 1e3 800 | ); 801 | println!("{avg_compression_ratio:.2} average compression ratio"); 802 | 803 | println!("\nChunks, n = {num_chunks}"); 804 | println!( 805 | "Average {avg_uncompressed_chunk_mb:.2} MB uncompressed, {avg_compressed_chunk_mb:.2} MB compressed" 806 | ); 807 | 808 | println!("\n{elapsed:.2?} elapsed"); 809 | } 810 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.21.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "ahash" 22 | version = "0.8.11" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 25 | dependencies = [ 26 | "cfg-if", 27 | "once_cell", 28 | "version_check", 29 | "zerocopy", 30 | ] 31 | 32 | [[package]] 33 | name = "aho-corasick" 34 | version = "1.1.3" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 37 | dependencies = [ 38 | "memchr", 39 | ] 40 | 41 | [[package]] 42 | name = "allocator-api2" 43 | version = "0.2.18" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" 46 | 47 | [[package]] 48 | name = "android-tzdata" 49 | version = "0.1.1" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 52 | 53 | [[package]] 54 | name = "android_system_properties" 55 | version = "0.1.5" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 58 | dependencies = [ 59 | "libc", 60 | ] 61 | 62 | [[package]] 63 | name = "anstream" 64 | version = "0.6.14" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" 67 | dependencies = [ 68 | "anstyle", 69 | "anstyle-parse", 70 | "anstyle-query", 71 | "anstyle-wincon", 72 | "colorchoice", 73 | "is_terminal_polyfill", 74 | "utf8parse", 75 | ] 76 | 77 | [[package]] 78 | name = "anstyle" 79 | version = "1.0.7" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" 82 | 83 | [[package]] 84 | name = "anstyle-parse" 85 | version = "0.2.4" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" 88 | dependencies = [ 89 | "utf8parse", 90 | ] 91 | 92 | [[package]] 93 | name = "anstyle-query" 94 | version = "1.1.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" 97 | dependencies = [ 98 | "windows-sys 0.52.0", 99 | ] 100 | 101 | [[package]] 102 | name = "anstyle-wincon" 103 | version = "3.0.3" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" 106 | dependencies = [ 107 | "anstyle", 108 | "windows-sys 0.52.0", 109 | ] 110 | 111 | [[package]] 112 | name = "anyhow" 113 | version = "1.0.81" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" 116 | 117 | [[package]] 118 | name = "approx" 119 | version = "0.5.1" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" 122 | dependencies = [ 123 | "num-traits", 124 | ] 125 | 126 | [[package]] 127 | name = "async-stream" 128 | version = "0.3.5" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" 131 | dependencies = [ 132 | "async-stream-impl", 133 | "futures-core", 134 | "pin-project-lite", 135 | ] 136 | 137 | [[package]] 138 | name = "async-stream-impl" 139 | version = "0.3.5" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" 142 | dependencies = [ 143 | "proc-macro2", 144 | "quote", 145 | "syn 2.0.53", 146 | ] 147 | 148 | [[package]] 149 | name = "async-trait" 150 | version = "0.1.79" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" 153 | dependencies = [ 154 | "proc-macro2", 155 | "quote", 156 | "syn 2.0.53", 157 | ] 158 | 159 | [[package]] 160 | name = "autocfg" 161 | version = "1.1.0" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 164 | 165 | [[package]] 166 | name = "axum" 167 | version = "0.6.20" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" 170 | dependencies = [ 171 | "async-trait", 172 | "axum-core", 173 | "bitflags 1.3.2", 174 | "bytes", 175 | "futures-util", 176 | "http 0.2.12", 177 | "http-body 0.4.6", 178 | "hyper 0.14.28", 179 | "itoa", 180 | "matchit", 181 | "memchr", 182 | "mime", 183 | "percent-encoding", 184 | "pin-project-lite", 185 | "rustversion", 186 | "serde", 187 | "sync_wrapper", 188 | "tower", 189 | "tower-layer", 190 | "tower-service", 191 | ] 192 | 193 | [[package]] 194 | name = "axum-core" 195 | version = "0.3.4" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" 198 | dependencies = [ 199 | "async-trait", 200 | "bytes", 201 | "futures-util", 202 | "http 0.2.12", 203 | "http-body 0.4.6", 204 | "mime", 205 | "rustversion", 206 | "tower-layer", 207 | "tower-service", 208 | ] 209 | 210 | [[package]] 211 | name = "backon" 212 | version = "0.4.4" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "d67782c3f868daa71d3533538e98a8e13713231969def7536e8039606fc46bf0" 215 | dependencies = [ 216 | "fastrand", 217 | "futures-core", 218 | "pin-project", 219 | "tokio", 220 | ] 221 | 222 | [[package]] 223 | name = "backtrace" 224 | version = "0.3.69" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" 227 | dependencies = [ 228 | "addr2line", 229 | "cc", 230 | "cfg-if", 231 | "libc", 232 | "miniz_oxide", 233 | "object", 234 | "rustc-demangle", 235 | ] 236 | 237 | [[package]] 238 | name = "base64" 239 | version = "0.21.7" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 242 | 243 | [[package]] 244 | name = "base64" 245 | version = "0.22.0" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" 248 | 249 | [[package]] 250 | name = "bindgen" 251 | version = "0.66.1" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" 254 | dependencies = [ 255 | "bitflags 2.5.0", 256 | "cexpr", 257 | "clang-sys", 258 | "lazy_static", 259 | "lazycell", 260 | "log", 261 | "peeking_take_while", 262 | "prettyplease", 263 | "proc-macro2", 264 | "quote", 265 | "regex", 266 | "rustc-hash", 267 | "shlex", 268 | "syn 2.0.53", 269 | "which", 270 | ] 271 | 272 | [[package]] 273 | name = "bitflags" 274 | version = "1.3.2" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 277 | 278 | [[package]] 279 | name = "bitflags" 280 | version = "2.5.0" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 283 | 284 | [[package]] 285 | name = "block-buffer" 286 | version = "0.10.4" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 289 | dependencies = [ 290 | "generic-array", 291 | ] 292 | 293 | [[package]] 294 | name = "blosc" 295 | version = "0.2.0" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "e35aa1ac22858b0fa4f4e74d96c84d93b86a27093989a3f2b812b00ff7c6d2fe" 298 | dependencies = [ 299 | "blosc-sys", 300 | "libc", 301 | ] 302 | 303 | [[package]] 304 | name = "blosc-sys" 305 | version = "1.21.0" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "76569aca625203c979c5c37343cdfdf4d2f9f4fd5f6a4eb89b68e7f2549d37e9" 308 | 309 | [[package]] 310 | name = "bumpalo" 311 | version = "3.16.0" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 314 | 315 | [[package]] 316 | name = "byteorder" 317 | version = "1.5.0" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 320 | 321 | [[package]] 322 | name = "bytes" 323 | version = "1.5.0" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" 326 | 327 | [[package]] 328 | name = "cached" 329 | version = "0.49.3" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "8e8e463fceca5674287f32d252fb1d94083758b8709c160efae66d263e5f4eba" 332 | dependencies = [ 333 | "ahash", 334 | "cached_proc_macro", 335 | "cached_proc_macro_types", 336 | "hashbrown 0.14.3", 337 | "instant", 338 | "once_cell", 339 | "thiserror", 340 | ] 341 | 342 | [[package]] 343 | name = "cached_proc_macro" 344 | version = "0.20.0" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "ad9f16c0d84de31a2ab7fdf5f7783c14631f7075cf464eb3bb43119f61c9cb2a" 347 | dependencies = [ 348 | "darling", 349 | "proc-macro2", 350 | "quote", 351 | "syn 1.0.109", 352 | ] 353 | 354 | [[package]] 355 | name = "cached_proc_macro_types" 356 | version = "0.1.1" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" 359 | 360 | [[package]] 361 | name = "cc" 362 | version = "1.0.90" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" 365 | 366 | [[package]] 367 | name = "cexpr" 368 | version = "0.6.0" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" 371 | dependencies = [ 372 | "nom", 373 | ] 374 | 375 | [[package]] 376 | name = "cfg-if" 377 | version = "1.0.0" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 380 | 381 | [[package]] 382 | name = "chrono" 383 | version = "0.4.35" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" 386 | dependencies = [ 387 | "android-tzdata", 388 | "iana-time-zone", 389 | "js-sys", 390 | "num-traits", 391 | "serde", 392 | "wasm-bindgen", 393 | "windows-targets 0.52.4", 394 | ] 395 | 396 | [[package]] 397 | name = "clang-sys" 398 | version = "1.7.0" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" 401 | dependencies = [ 402 | "glob", 403 | "libc", 404 | "libloading", 405 | ] 406 | 407 | [[package]] 408 | name = "clap" 409 | version = "4.5.7" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" 412 | dependencies = [ 413 | "clap_builder", 414 | "clap_derive", 415 | ] 416 | 417 | [[package]] 418 | name = "clap_builder" 419 | version = "4.5.7" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" 422 | dependencies = [ 423 | "anstream", 424 | "anstyle", 425 | "clap_lex", 426 | "strsim 0.11.1", 427 | ] 428 | 429 | [[package]] 430 | name = "clap_derive" 431 | version = "4.5.5" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" 434 | dependencies = [ 435 | "heck 0.5.0", 436 | "proc-macro2", 437 | "quote", 438 | "syn 2.0.53", 439 | ] 440 | 441 | [[package]] 442 | name = "clap_lex" 443 | version = "0.7.1" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" 446 | 447 | [[package]] 448 | name = "colorchoice" 449 | version = "1.0.1" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" 452 | 453 | [[package]] 454 | name = "console-api" 455 | version = "0.6.0" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "fd326812b3fd01da5bb1af7d340d0d555fd3d4b641e7f1dfcf5962a902952787" 458 | dependencies = [ 459 | "futures-core", 460 | "prost", 461 | "prost-types", 462 | "tonic", 463 | "tracing-core", 464 | ] 465 | 466 | [[package]] 467 | name = "console-subscriber" 468 | version = "0.2.0" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "7481d4c57092cd1c19dd541b92bdce883de840df30aa5d03fd48a3935c01842e" 471 | dependencies = [ 472 | "console-api", 473 | "crossbeam-channel", 474 | "crossbeam-utils", 475 | "futures-task", 476 | "hdrhistogram", 477 | "humantime", 478 | "prost-types", 479 | "serde", 480 | "serde_json", 481 | "thread_local", 482 | "tokio", 483 | "tokio-stream", 484 | "tonic", 485 | "tracing", 486 | "tracing-core", 487 | "tracing-subscriber", 488 | ] 489 | 490 | [[package]] 491 | name = "core-foundation" 492 | version = "0.9.4" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 495 | dependencies = [ 496 | "core-foundation-sys", 497 | "libc", 498 | ] 499 | 500 | [[package]] 501 | name = "core-foundation-sys" 502 | version = "0.8.6" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 505 | 506 | [[package]] 507 | name = "crc32fast" 508 | version = "1.4.0" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" 511 | dependencies = [ 512 | "cfg-if", 513 | ] 514 | 515 | [[package]] 516 | name = "crossbeam-channel" 517 | version = "0.5.12" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" 520 | dependencies = [ 521 | "crossbeam-utils", 522 | ] 523 | 524 | [[package]] 525 | name = "crossbeam-utils" 526 | version = "0.8.19" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" 529 | 530 | [[package]] 531 | name = "crypto-common" 532 | version = "0.1.6" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 535 | dependencies = [ 536 | "generic-array", 537 | "typenum", 538 | ] 539 | 540 | [[package]] 541 | name = "darling" 542 | version = "0.14.4" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" 545 | dependencies = [ 546 | "darling_core", 547 | "darling_macro", 548 | ] 549 | 550 | [[package]] 551 | name = "darling_core" 552 | version = "0.14.4" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" 555 | dependencies = [ 556 | "fnv", 557 | "ident_case", 558 | "proc-macro2", 559 | "quote", 560 | "strsim 0.10.0", 561 | "syn 1.0.109", 562 | ] 563 | 564 | [[package]] 565 | name = "darling_macro" 566 | version = "0.14.4" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" 569 | dependencies = [ 570 | "darling_core", 571 | "quote", 572 | "syn 1.0.109", 573 | ] 574 | 575 | [[package]] 576 | name = "digest" 577 | version = "0.10.7" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 580 | dependencies = [ 581 | "block-buffer", 582 | "crypto-common", 583 | ] 584 | 585 | [[package]] 586 | name = "doc-comment" 587 | version = "0.3.3" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" 590 | 591 | [[package]] 592 | name = "either" 593 | version = "1.10.0" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" 596 | 597 | [[package]] 598 | name = "encoding_rs" 599 | version = "0.8.34" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" 602 | dependencies = [ 603 | "cfg-if", 604 | ] 605 | 606 | [[package]] 607 | name = "equivalent" 608 | version = "1.0.1" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 611 | 612 | [[package]] 613 | name = "errno" 614 | version = "0.3.8" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" 617 | dependencies = [ 618 | "libc", 619 | "windows-sys 0.52.0", 620 | ] 621 | 622 | [[package]] 623 | name = "fastrand" 624 | version = "2.0.2" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" 627 | 628 | [[package]] 629 | name = "flate2" 630 | version = "1.0.30" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" 633 | dependencies = [ 634 | "crc32fast", 635 | "miniz_oxide", 636 | ] 637 | 638 | [[package]] 639 | name = "fnv" 640 | version = "1.0.7" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 643 | 644 | [[package]] 645 | name = "foreign-types" 646 | version = "0.3.2" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 649 | dependencies = [ 650 | "foreign-types-shared", 651 | ] 652 | 653 | [[package]] 654 | name = "foreign-types-shared" 655 | version = "0.1.1" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 658 | 659 | [[package]] 660 | name = "form_urlencoded" 661 | version = "1.2.1" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 664 | dependencies = [ 665 | "percent-encoding", 666 | ] 667 | 668 | [[package]] 669 | name = "futures" 670 | version = "0.3.30" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 673 | dependencies = [ 674 | "futures-channel", 675 | "futures-core", 676 | "futures-executor", 677 | "futures-io", 678 | "futures-sink", 679 | "futures-task", 680 | "futures-util", 681 | ] 682 | 683 | [[package]] 684 | name = "futures-channel" 685 | version = "0.3.30" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 688 | dependencies = [ 689 | "futures-core", 690 | "futures-sink", 691 | ] 692 | 693 | [[package]] 694 | name = "futures-core" 695 | version = "0.3.30" 696 | source = "registry+https://github.com/rust-lang/crates.io-index" 697 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 698 | 699 | [[package]] 700 | name = "futures-executor" 701 | version = "0.3.30" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" 704 | dependencies = [ 705 | "futures-core", 706 | "futures-task", 707 | "futures-util", 708 | ] 709 | 710 | [[package]] 711 | name = "futures-io" 712 | version = "0.3.30" 713 | source = "registry+https://github.com/rust-lang/crates.io-index" 714 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 715 | 716 | [[package]] 717 | name = "futures-macro" 718 | version = "0.3.30" 719 | source = "registry+https://github.com/rust-lang/crates.io-index" 720 | checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 721 | dependencies = [ 722 | "proc-macro2", 723 | "quote", 724 | "syn 2.0.53", 725 | ] 726 | 727 | [[package]] 728 | name = "futures-sink" 729 | version = "0.3.30" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 732 | 733 | [[package]] 734 | name = "futures-task" 735 | version = "0.3.30" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 738 | 739 | [[package]] 740 | name = "futures-util" 741 | version = "0.3.30" 742 | source = "registry+https://github.com/rust-lang/crates.io-index" 743 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 744 | dependencies = [ 745 | "futures-channel", 746 | "futures-core", 747 | "futures-io", 748 | "futures-macro", 749 | "futures-sink", 750 | "futures-task", 751 | "memchr", 752 | "pin-project-lite", 753 | "pin-utils", 754 | "slab", 755 | ] 756 | 757 | [[package]] 758 | name = "gdal" 759 | version = "0.16.0" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "6639365794fa1f35f36f8cd3e2ea21c94c9b04aa8d9ee0d0f3324621efeb0800" 762 | dependencies = [ 763 | "bitflags 2.5.0", 764 | "chrono", 765 | "gdal-sys", 766 | "geo-types", 767 | "libc", 768 | "ndarray", 769 | "once_cell", 770 | "semver", 771 | "thiserror", 772 | ] 773 | 774 | [[package]] 775 | name = "gdal-sys" 776 | version = "0.9.1" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "ab73a6c9d0d620fb5b07e02ffbb8ae08f053299cbe5db0c1bbe1d58cb33e3f38" 779 | dependencies = [ 780 | "bindgen", 781 | "libc", 782 | "pkg-config", 783 | "semver", 784 | ] 785 | 786 | [[package]] 787 | name = "generic-array" 788 | version = "0.14.7" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 791 | dependencies = [ 792 | "typenum", 793 | "version_check", 794 | ] 795 | 796 | [[package]] 797 | name = "geo-types" 798 | version = "0.7.13" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "9ff16065e5720f376fbced200a5ae0f47ace85fd70b7e54269790281353b6d61" 801 | dependencies = [ 802 | "approx", 803 | "num-traits", 804 | "serde", 805 | ] 806 | 807 | [[package]] 808 | name = "getrandom" 809 | version = "0.2.14" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" 812 | dependencies = [ 813 | "cfg-if", 814 | "js-sys", 815 | "libc", 816 | "wasi", 817 | "wasm-bindgen", 818 | ] 819 | 820 | [[package]] 821 | name = "gimli" 822 | version = "0.28.1" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 825 | 826 | [[package]] 827 | name = "glob" 828 | version = "0.3.1" 829 | source = "registry+https://github.com/rust-lang/crates.io-index" 830 | checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" 831 | 832 | [[package]] 833 | name = "h2" 834 | version = "0.3.26" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" 837 | dependencies = [ 838 | "bytes", 839 | "fnv", 840 | "futures-core", 841 | "futures-sink", 842 | "futures-util", 843 | "http 0.2.12", 844 | "indexmap 2.2.6", 845 | "slab", 846 | "tokio", 847 | "tokio-util", 848 | "tracing", 849 | ] 850 | 851 | [[package]] 852 | name = "h2" 853 | version = "0.4.4" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" 856 | dependencies = [ 857 | "bytes", 858 | "fnv", 859 | "futures-core", 860 | "futures-sink", 861 | "futures-util", 862 | "http 1.1.0", 863 | "indexmap 2.2.6", 864 | "slab", 865 | "tokio", 866 | "tokio-util", 867 | "tracing", 868 | ] 869 | 870 | [[package]] 871 | name = "hashbrown" 872 | version = "0.12.3" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 875 | 876 | [[package]] 877 | name = "hashbrown" 878 | version = "0.14.3" 879 | source = "registry+https://github.com/rust-lang/crates.io-index" 880 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 881 | dependencies = [ 882 | "ahash", 883 | "allocator-api2", 884 | ] 885 | 886 | [[package]] 887 | name = "hdrhistogram" 888 | version = "7.5.4" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" 891 | dependencies = [ 892 | "base64 0.21.7", 893 | "byteorder", 894 | "flate2", 895 | "nom", 896 | "num-traits", 897 | ] 898 | 899 | [[package]] 900 | name = "heck" 901 | version = "0.4.1" 902 | source = "registry+https://github.com/rust-lang/crates.io-index" 903 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 904 | 905 | [[package]] 906 | name = "heck" 907 | version = "0.5.0" 908 | source = "registry+https://github.com/rust-lang/crates.io-index" 909 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 910 | 911 | [[package]] 912 | name = "hermit-abi" 913 | version = "0.3.9" 914 | source = "registry+https://github.com/rust-lang/crates.io-index" 915 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 916 | 917 | [[package]] 918 | name = "home" 919 | version = "0.5.9" 920 | source = "registry+https://github.com/rust-lang/crates.io-index" 921 | checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" 922 | dependencies = [ 923 | "windows-sys 0.52.0", 924 | ] 925 | 926 | [[package]] 927 | name = "http" 928 | version = "0.2.12" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" 931 | dependencies = [ 932 | "bytes", 933 | "fnv", 934 | "itoa", 935 | ] 936 | 937 | [[package]] 938 | name = "http" 939 | version = "1.1.0" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" 942 | dependencies = [ 943 | "bytes", 944 | "fnv", 945 | "itoa", 946 | ] 947 | 948 | [[package]] 949 | name = "http-body" 950 | version = "0.4.6" 951 | source = "registry+https://github.com/rust-lang/crates.io-index" 952 | checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" 953 | dependencies = [ 954 | "bytes", 955 | "http 0.2.12", 956 | "pin-project-lite", 957 | ] 958 | 959 | [[package]] 960 | name = "http-body" 961 | version = "1.0.0" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" 964 | dependencies = [ 965 | "bytes", 966 | "http 1.1.0", 967 | ] 968 | 969 | [[package]] 970 | name = "http-body-util" 971 | version = "0.1.1" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" 974 | dependencies = [ 975 | "bytes", 976 | "futures-core", 977 | "http 1.1.0", 978 | "http-body 1.0.0", 979 | "pin-project-lite", 980 | ] 981 | 982 | [[package]] 983 | name = "httparse" 984 | version = "1.8.0" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 987 | 988 | [[package]] 989 | name = "httpdate" 990 | version = "1.0.3" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 993 | 994 | [[package]] 995 | name = "humantime" 996 | version = "2.1.0" 997 | source = "registry+https://github.com/rust-lang/crates.io-index" 998 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 999 | 1000 | [[package]] 1001 | name = "hyper" 1002 | version = "0.14.28" 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" 1004 | checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" 1005 | dependencies = [ 1006 | "bytes", 1007 | "futures-channel", 1008 | "futures-core", 1009 | "futures-util", 1010 | "h2 0.3.26", 1011 | "http 0.2.12", 1012 | "http-body 0.4.6", 1013 | "httparse", 1014 | "httpdate", 1015 | "itoa", 1016 | "pin-project-lite", 1017 | "socket2", 1018 | "tokio", 1019 | "tower-service", 1020 | "tracing", 1021 | "want", 1022 | ] 1023 | 1024 | [[package]] 1025 | name = "hyper" 1026 | version = "1.2.0" 1027 | source = "registry+https://github.com/rust-lang/crates.io-index" 1028 | checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" 1029 | dependencies = [ 1030 | "bytes", 1031 | "futures-channel", 1032 | "futures-util", 1033 | "h2 0.4.4", 1034 | "http 1.1.0", 1035 | "http-body 1.0.0", 1036 | "httparse", 1037 | "itoa", 1038 | "pin-project-lite", 1039 | "smallvec", 1040 | "tokio", 1041 | "want", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "hyper-rustls" 1046 | version = "0.26.0" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" 1049 | dependencies = [ 1050 | "futures-util", 1051 | "http 1.1.0", 1052 | "hyper 1.2.0", 1053 | "hyper-util", 1054 | "rustls", 1055 | "rustls-pki-types", 1056 | "tokio", 1057 | "tokio-rustls", 1058 | "tower-service", 1059 | ] 1060 | 1061 | [[package]] 1062 | name = "hyper-timeout" 1063 | version = "0.4.1" 1064 | source = "registry+https://github.com/rust-lang/crates.io-index" 1065 | checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" 1066 | dependencies = [ 1067 | "hyper 0.14.28", 1068 | "pin-project-lite", 1069 | "tokio", 1070 | "tokio-io-timeout", 1071 | ] 1072 | 1073 | [[package]] 1074 | name = "hyper-tls" 1075 | version = "0.6.0" 1076 | source = "registry+https://github.com/rust-lang/crates.io-index" 1077 | checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 1078 | dependencies = [ 1079 | "bytes", 1080 | "http-body-util", 1081 | "hyper 1.2.0", 1082 | "hyper-util", 1083 | "native-tls", 1084 | "tokio", 1085 | "tokio-native-tls", 1086 | "tower-service", 1087 | ] 1088 | 1089 | [[package]] 1090 | name = "hyper-util" 1091 | version = "0.1.3" 1092 | source = "registry+https://github.com/rust-lang/crates.io-index" 1093 | checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" 1094 | dependencies = [ 1095 | "bytes", 1096 | "futures-channel", 1097 | "futures-util", 1098 | "http 1.1.0", 1099 | "http-body 1.0.0", 1100 | "hyper 1.2.0", 1101 | "pin-project-lite", 1102 | "socket2", 1103 | "tokio", 1104 | "tower", 1105 | "tower-service", 1106 | "tracing", 1107 | ] 1108 | 1109 | [[package]] 1110 | name = "iana-time-zone" 1111 | version = "0.1.60" 1112 | source = "registry+https://github.com/rust-lang/crates.io-index" 1113 | checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" 1114 | dependencies = [ 1115 | "android_system_properties", 1116 | "core-foundation-sys", 1117 | "iana-time-zone-haiku", 1118 | "js-sys", 1119 | "wasm-bindgen", 1120 | "windows-core", 1121 | ] 1122 | 1123 | [[package]] 1124 | name = "iana-time-zone-haiku" 1125 | version = "0.1.2" 1126 | source = "registry+https://github.com/rust-lang/crates.io-index" 1127 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 1128 | dependencies = [ 1129 | "cc", 1130 | ] 1131 | 1132 | [[package]] 1133 | name = "ident_case" 1134 | version = "1.0.1" 1135 | source = "registry+https://github.com/rust-lang/crates.io-index" 1136 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 1137 | 1138 | [[package]] 1139 | name = "idna" 1140 | version = "0.5.0" 1141 | source = "registry+https://github.com/rust-lang/crates.io-index" 1142 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 1143 | dependencies = [ 1144 | "unicode-bidi", 1145 | "unicode-normalization", 1146 | ] 1147 | 1148 | [[package]] 1149 | name = "indexmap" 1150 | version = "1.9.3" 1151 | source = "registry+https://github.com/rust-lang/crates.io-index" 1152 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 1153 | dependencies = [ 1154 | "autocfg", 1155 | "hashbrown 0.12.3", 1156 | ] 1157 | 1158 | [[package]] 1159 | name = "indexmap" 1160 | version = "2.2.6" 1161 | source = "registry+https://github.com/rust-lang/crates.io-index" 1162 | checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" 1163 | dependencies = [ 1164 | "equivalent", 1165 | "hashbrown 0.14.3", 1166 | ] 1167 | 1168 | [[package]] 1169 | name = "instant" 1170 | version = "0.1.12" 1171 | source = "registry+https://github.com/rust-lang/crates.io-index" 1172 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 1173 | dependencies = [ 1174 | "cfg-if", 1175 | "js-sys", 1176 | "wasm-bindgen", 1177 | "web-sys", 1178 | ] 1179 | 1180 | [[package]] 1181 | name = "ipnet" 1182 | version = "2.9.0" 1183 | source = "registry+https://github.com/rust-lang/crates.io-index" 1184 | checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" 1185 | 1186 | [[package]] 1187 | name = "is_terminal_polyfill" 1188 | version = "1.70.0" 1189 | source = "registry+https://github.com/rust-lang/crates.io-index" 1190 | checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" 1191 | 1192 | [[package]] 1193 | name = "itertools" 1194 | version = "0.12.1" 1195 | source = "registry+https://github.com/rust-lang/crates.io-index" 1196 | checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 1197 | dependencies = [ 1198 | "either", 1199 | ] 1200 | 1201 | [[package]] 1202 | name = "itoa" 1203 | version = "1.0.11" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 1206 | 1207 | [[package]] 1208 | name = "js-sys" 1209 | version = "0.3.69" 1210 | source = "registry+https://github.com/rust-lang/crates.io-index" 1211 | checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" 1212 | dependencies = [ 1213 | "wasm-bindgen", 1214 | ] 1215 | 1216 | [[package]] 1217 | name = "lazy_static" 1218 | version = "1.4.0" 1219 | source = "registry+https://github.com/rust-lang/crates.io-index" 1220 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 1221 | 1222 | [[package]] 1223 | name = "lazycell" 1224 | version = "1.3.0" 1225 | source = "registry+https://github.com/rust-lang/crates.io-index" 1226 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 1227 | 1228 | [[package]] 1229 | name = "libc" 1230 | version = "0.2.153" 1231 | source = "registry+https://github.com/rust-lang/crates.io-index" 1232 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 1233 | 1234 | [[package]] 1235 | name = "libloading" 1236 | version = "0.8.3" 1237 | source = "registry+https://github.com/rust-lang/crates.io-index" 1238 | checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" 1239 | dependencies = [ 1240 | "cfg-if", 1241 | "windows-targets 0.52.4", 1242 | ] 1243 | 1244 | [[package]] 1245 | name = "libm" 1246 | version = "0.2.8" 1247 | source = "registry+https://github.com/rust-lang/crates.io-index" 1248 | checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" 1249 | 1250 | [[package]] 1251 | name = "linux-raw-sys" 1252 | version = "0.4.13" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" 1255 | 1256 | [[package]] 1257 | name = "lock_api" 1258 | version = "0.4.11" 1259 | source = "registry+https://github.com/rust-lang/crates.io-index" 1260 | checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" 1261 | dependencies = [ 1262 | "autocfg", 1263 | "scopeguard", 1264 | ] 1265 | 1266 | [[package]] 1267 | name = "log" 1268 | version = "0.4.21" 1269 | source = "registry+https://github.com/rust-lang/crates.io-index" 1270 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 1271 | 1272 | [[package]] 1273 | name = "matchers" 1274 | version = "0.1.0" 1275 | source = "registry+https://github.com/rust-lang/crates.io-index" 1276 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 1277 | dependencies = [ 1278 | "regex-automata 0.1.10", 1279 | ] 1280 | 1281 | [[package]] 1282 | name = "matchit" 1283 | version = "0.7.3" 1284 | source = "registry+https://github.com/rust-lang/crates.io-index" 1285 | checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" 1286 | 1287 | [[package]] 1288 | name = "matrixmultiply" 1289 | version = "0.3.8" 1290 | source = "registry+https://github.com/rust-lang/crates.io-index" 1291 | checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" 1292 | dependencies = [ 1293 | "autocfg", 1294 | "rawpointer", 1295 | ] 1296 | 1297 | [[package]] 1298 | name = "md-5" 1299 | version = "0.10.6" 1300 | source = "registry+https://github.com/rust-lang/crates.io-index" 1301 | checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" 1302 | dependencies = [ 1303 | "cfg-if", 1304 | "digest", 1305 | ] 1306 | 1307 | [[package]] 1308 | name = "memchr" 1309 | version = "2.7.1" 1310 | source = "registry+https://github.com/rust-lang/crates.io-index" 1311 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 1312 | 1313 | [[package]] 1314 | name = "mime" 1315 | version = "0.3.17" 1316 | source = "registry+https://github.com/rust-lang/crates.io-index" 1317 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 1318 | 1319 | [[package]] 1320 | name = "minimal-lexical" 1321 | version = "0.2.1" 1322 | source = "registry+https://github.com/rust-lang/crates.io-index" 1323 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 1324 | 1325 | [[package]] 1326 | name = "miniz_oxide" 1327 | version = "0.7.2" 1328 | source = "registry+https://github.com/rust-lang/crates.io-index" 1329 | checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" 1330 | dependencies = [ 1331 | "adler", 1332 | ] 1333 | 1334 | [[package]] 1335 | name = "mio" 1336 | version = "0.8.11" 1337 | source = "registry+https://github.com/rust-lang/crates.io-index" 1338 | checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" 1339 | dependencies = [ 1340 | "libc", 1341 | "wasi", 1342 | "windows-sys 0.48.0", 1343 | ] 1344 | 1345 | [[package]] 1346 | name = "native-tls" 1347 | version = "0.2.11" 1348 | source = "registry+https://github.com/rust-lang/crates.io-index" 1349 | checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" 1350 | dependencies = [ 1351 | "lazy_static", 1352 | "libc", 1353 | "log", 1354 | "openssl", 1355 | "openssl-probe", 1356 | "openssl-sys", 1357 | "schannel", 1358 | "security-framework", 1359 | "security-framework-sys", 1360 | "tempfile", 1361 | ] 1362 | 1363 | [[package]] 1364 | name = "ndarray" 1365 | version = "0.15.6" 1366 | source = "registry+https://github.com/rust-lang/crates.io-index" 1367 | checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32" 1368 | dependencies = [ 1369 | "matrixmultiply", 1370 | "num-complex", 1371 | "num-integer", 1372 | "num-traits", 1373 | "rawpointer", 1374 | ] 1375 | 1376 | [[package]] 1377 | name = "ndarray-interp" 1378 | version = "0.4.1" 1379 | source = "registry+https://github.com/rust-lang/crates.io-index" 1380 | checksum = "8158249c71147ce8818363aa76fb7590f4239988265150960132a16cbe0c6fda" 1381 | dependencies = [ 1382 | "ndarray", 1383 | "num-traits", 1384 | "thiserror", 1385 | ] 1386 | 1387 | [[package]] 1388 | name = "nom" 1389 | version = "7.1.3" 1390 | source = "registry+https://github.com/rust-lang/crates.io-index" 1391 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 1392 | dependencies = [ 1393 | "memchr", 1394 | "minimal-lexical", 1395 | ] 1396 | 1397 | [[package]] 1398 | name = "num-complex" 1399 | version = "0.4.5" 1400 | source = "registry+https://github.com/rust-lang/crates.io-index" 1401 | checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" 1402 | dependencies = [ 1403 | "num-traits", 1404 | ] 1405 | 1406 | [[package]] 1407 | name = "num-integer" 1408 | version = "0.1.46" 1409 | source = "registry+https://github.com/rust-lang/crates.io-index" 1410 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 1411 | dependencies = [ 1412 | "num-traits", 1413 | ] 1414 | 1415 | [[package]] 1416 | name = "num-traits" 1417 | version = "0.2.18" 1418 | source = "registry+https://github.com/rust-lang/crates.io-index" 1419 | checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" 1420 | dependencies = [ 1421 | "autocfg", 1422 | "libm", 1423 | ] 1424 | 1425 | [[package]] 1426 | name = "num_cpus" 1427 | version = "1.16.0" 1428 | source = "registry+https://github.com/rust-lang/crates.io-index" 1429 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 1430 | dependencies = [ 1431 | "hermit-abi", 1432 | "libc", 1433 | ] 1434 | 1435 | [[package]] 1436 | name = "object" 1437 | version = "0.32.2" 1438 | source = "registry+https://github.com/rust-lang/crates.io-index" 1439 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 1440 | dependencies = [ 1441 | "memchr", 1442 | ] 1443 | 1444 | [[package]] 1445 | name = "object_store" 1446 | version = "0.10.0" 1447 | source = "registry+https://github.com/rust-lang/crates.io-index" 1448 | checksum = "9cda97d37376c28c508b0abc56b855c5e700324e9b753226681e658c833675c5" 1449 | dependencies = [ 1450 | "async-trait", 1451 | "base64 0.22.0", 1452 | "bytes", 1453 | "chrono", 1454 | "futures", 1455 | "humantime", 1456 | "hyper 1.2.0", 1457 | "itertools", 1458 | "md-5", 1459 | "parking_lot 0.12.1", 1460 | "percent-encoding", 1461 | "quick-xml", 1462 | "rand", 1463 | "reqwest", 1464 | "ring", 1465 | "serde", 1466 | "serde_json", 1467 | "snafu", 1468 | "tokio", 1469 | "tracing", 1470 | "url", 1471 | "walkdir", 1472 | ] 1473 | 1474 | [[package]] 1475 | name = "once_cell" 1476 | version = "1.19.0" 1477 | source = "registry+https://github.com/rust-lang/crates.io-index" 1478 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 1479 | 1480 | [[package]] 1481 | name = "openssl" 1482 | version = "0.10.64" 1483 | source = "registry+https://github.com/rust-lang/crates.io-index" 1484 | checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" 1485 | dependencies = [ 1486 | "bitflags 2.5.0", 1487 | "cfg-if", 1488 | "foreign-types", 1489 | "libc", 1490 | "once_cell", 1491 | "openssl-macros", 1492 | "openssl-sys", 1493 | ] 1494 | 1495 | [[package]] 1496 | name = "openssl-macros" 1497 | version = "0.1.1" 1498 | source = "registry+https://github.com/rust-lang/crates.io-index" 1499 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 1500 | dependencies = [ 1501 | "proc-macro2", 1502 | "quote", 1503 | "syn 2.0.53", 1504 | ] 1505 | 1506 | [[package]] 1507 | name = "openssl-probe" 1508 | version = "0.1.5" 1509 | source = "registry+https://github.com/rust-lang/crates.io-index" 1510 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 1511 | 1512 | [[package]] 1513 | name = "openssl-sys" 1514 | version = "0.9.102" 1515 | source = "registry+https://github.com/rust-lang/crates.io-index" 1516 | checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" 1517 | dependencies = [ 1518 | "cc", 1519 | "libc", 1520 | "pkg-config", 1521 | "vcpkg", 1522 | ] 1523 | 1524 | [[package]] 1525 | name = "parking_lot" 1526 | version = "0.11.2" 1527 | source = "registry+https://github.com/rust-lang/crates.io-index" 1528 | checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" 1529 | dependencies = [ 1530 | "instant", 1531 | "lock_api", 1532 | "parking_lot_core 0.8.6", 1533 | ] 1534 | 1535 | [[package]] 1536 | name = "parking_lot" 1537 | version = "0.12.1" 1538 | source = "registry+https://github.com/rust-lang/crates.io-index" 1539 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 1540 | dependencies = [ 1541 | "lock_api", 1542 | "parking_lot_core 0.9.9", 1543 | ] 1544 | 1545 | [[package]] 1546 | name = "parking_lot_core" 1547 | version = "0.8.6" 1548 | source = "registry+https://github.com/rust-lang/crates.io-index" 1549 | checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" 1550 | dependencies = [ 1551 | "cfg-if", 1552 | "instant", 1553 | "libc", 1554 | "redox_syscall 0.2.16", 1555 | "smallvec", 1556 | "winapi", 1557 | ] 1558 | 1559 | [[package]] 1560 | name = "parking_lot_core" 1561 | version = "0.9.9" 1562 | source = "registry+https://github.com/rust-lang/crates.io-index" 1563 | checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" 1564 | dependencies = [ 1565 | "cfg-if", 1566 | "libc", 1567 | "redox_syscall 0.4.1", 1568 | "smallvec", 1569 | "windows-targets 0.48.5", 1570 | ] 1571 | 1572 | [[package]] 1573 | name = "peeking_take_while" 1574 | version = "0.1.2" 1575 | source = "registry+https://github.com/rust-lang/crates.io-index" 1576 | checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" 1577 | 1578 | [[package]] 1579 | name = "percent-encoding" 1580 | version = "2.3.1" 1581 | source = "registry+https://github.com/rust-lang/crates.io-index" 1582 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1583 | 1584 | [[package]] 1585 | name = "pin-project" 1586 | version = "1.1.5" 1587 | source = "registry+https://github.com/rust-lang/crates.io-index" 1588 | checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" 1589 | dependencies = [ 1590 | "pin-project-internal", 1591 | ] 1592 | 1593 | [[package]] 1594 | name = "pin-project-internal" 1595 | version = "1.1.5" 1596 | source = "registry+https://github.com/rust-lang/crates.io-index" 1597 | checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" 1598 | dependencies = [ 1599 | "proc-macro2", 1600 | "quote", 1601 | "syn 2.0.53", 1602 | ] 1603 | 1604 | [[package]] 1605 | name = "pin-project-lite" 1606 | version = "0.2.13" 1607 | source = "registry+https://github.com/rust-lang/crates.io-index" 1608 | checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" 1609 | 1610 | [[package]] 1611 | name = "pin-utils" 1612 | version = "0.1.0" 1613 | source = "registry+https://github.com/rust-lang/crates.io-index" 1614 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1615 | 1616 | [[package]] 1617 | name = "pkg-config" 1618 | version = "0.3.30" 1619 | source = "registry+https://github.com/rust-lang/crates.io-index" 1620 | checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 1621 | 1622 | [[package]] 1623 | name = "ppv-lite86" 1624 | version = "0.2.17" 1625 | source = "registry+https://github.com/rust-lang/crates.io-index" 1626 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 1627 | 1628 | [[package]] 1629 | name = "prettyplease" 1630 | version = "0.2.17" 1631 | source = "registry+https://github.com/rust-lang/crates.io-index" 1632 | checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" 1633 | dependencies = [ 1634 | "proc-macro2", 1635 | "syn 2.0.53", 1636 | ] 1637 | 1638 | [[package]] 1639 | name = "proc-macro2" 1640 | version = "1.0.79" 1641 | source = "registry+https://github.com/rust-lang/crates.io-index" 1642 | checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" 1643 | dependencies = [ 1644 | "unicode-ident", 1645 | ] 1646 | 1647 | [[package]] 1648 | name = "prost" 1649 | version = "0.12.4" 1650 | source = "registry+https://github.com/rust-lang/crates.io-index" 1651 | checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" 1652 | dependencies = [ 1653 | "bytes", 1654 | "prost-derive", 1655 | ] 1656 | 1657 | [[package]] 1658 | name = "prost-derive" 1659 | version = "0.12.4" 1660 | source = "registry+https://github.com/rust-lang/crates.io-index" 1661 | checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" 1662 | dependencies = [ 1663 | "anyhow", 1664 | "itertools", 1665 | "proc-macro2", 1666 | "quote", 1667 | "syn 2.0.53", 1668 | ] 1669 | 1670 | [[package]] 1671 | name = "prost-types" 1672 | version = "0.12.4" 1673 | source = "registry+https://github.com/rust-lang/crates.io-index" 1674 | checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" 1675 | dependencies = [ 1676 | "prost", 1677 | ] 1678 | 1679 | [[package]] 1680 | name = "quick-xml" 1681 | version = "0.31.0" 1682 | source = "registry+https://github.com/rust-lang/crates.io-index" 1683 | checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" 1684 | dependencies = [ 1685 | "memchr", 1686 | "serde", 1687 | ] 1688 | 1689 | [[package]] 1690 | name = "quote" 1691 | version = "1.0.35" 1692 | source = "registry+https://github.com/rust-lang/crates.io-index" 1693 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 1694 | dependencies = [ 1695 | "proc-macro2", 1696 | ] 1697 | 1698 | [[package]] 1699 | name = "rand" 1700 | version = "0.8.5" 1701 | source = "registry+https://github.com/rust-lang/crates.io-index" 1702 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1703 | dependencies = [ 1704 | "libc", 1705 | "rand_chacha", 1706 | "rand_core", 1707 | ] 1708 | 1709 | [[package]] 1710 | name = "rand_chacha" 1711 | version = "0.3.1" 1712 | source = "registry+https://github.com/rust-lang/crates.io-index" 1713 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1714 | dependencies = [ 1715 | "ppv-lite86", 1716 | "rand_core", 1717 | ] 1718 | 1719 | [[package]] 1720 | name = "rand_core" 1721 | version = "0.6.4" 1722 | source = "registry+https://github.com/rust-lang/crates.io-index" 1723 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1724 | dependencies = [ 1725 | "getrandom", 1726 | ] 1727 | 1728 | [[package]] 1729 | name = "rawpointer" 1730 | version = "0.2.1" 1731 | source = "registry+https://github.com/rust-lang/crates.io-index" 1732 | checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" 1733 | 1734 | [[package]] 1735 | name = "redox_syscall" 1736 | version = "0.2.16" 1737 | source = "registry+https://github.com/rust-lang/crates.io-index" 1738 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 1739 | dependencies = [ 1740 | "bitflags 1.3.2", 1741 | ] 1742 | 1743 | [[package]] 1744 | name = "redox_syscall" 1745 | version = "0.4.1" 1746 | source = "registry+https://github.com/rust-lang/crates.io-index" 1747 | checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" 1748 | dependencies = [ 1749 | "bitflags 1.3.2", 1750 | ] 1751 | 1752 | [[package]] 1753 | name = "reformatters" 1754 | version = "0.1.0" 1755 | dependencies = [ 1756 | "anyhow", 1757 | "backon", 1758 | "blosc", 1759 | "cached", 1760 | "chrono", 1761 | "clap", 1762 | "console-subscriber", 1763 | "futures", 1764 | "gdal", 1765 | "gdal-sys", 1766 | "itertools", 1767 | "ndarray", 1768 | "ndarray-interp", 1769 | "object_store", 1770 | "once_cell", 1771 | "regex", 1772 | "reqwest", 1773 | "reqwest-middleware", 1774 | "reqwest-retry", 1775 | "serde", 1776 | "serde_json", 1777 | "tokio", 1778 | "tokio-util", 1779 | "url", 1780 | ] 1781 | 1782 | [[package]] 1783 | name = "regex" 1784 | version = "1.10.4" 1785 | source = "registry+https://github.com/rust-lang/crates.io-index" 1786 | checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" 1787 | dependencies = [ 1788 | "aho-corasick", 1789 | "memchr", 1790 | "regex-automata 0.4.6", 1791 | "regex-syntax 0.8.3", 1792 | ] 1793 | 1794 | [[package]] 1795 | name = "regex-automata" 1796 | version = "0.1.10" 1797 | source = "registry+https://github.com/rust-lang/crates.io-index" 1798 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 1799 | dependencies = [ 1800 | "regex-syntax 0.6.29", 1801 | ] 1802 | 1803 | [[package]] 1804 | name = "regex-automata" 1805 | version = "0.4.6" 1806 | source = "registry+https://github.com/rust-lang/crates.io-index" 1807 | checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" 1808 | dependencies = [ 1809 | "aho-corasick", 1810 | "memchr", 1811 | "regex-syntax 0.8.3", 1812 | ] 1813 | 1814 | [[package]] 1815 | name = "regex-syntax" 1816 | version = "0.6.29" 1817 | source = "registry+https://github.com/rust-lang/crates.io-index" 1818 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 1819 | 1820 | [[package]] 1821 | name = "regex-syntax" 1822 | version = "0.8.3" 1823 | source = "registry+https://github.com/rust-lang/crates.io-index" 1824 | checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" 1825 | 1826 | [[package]] 1827 | name = "reqwest" 1828 | version = "0.12.3" 1829 | source = "registry+https://github.com/rust-lang/crates.io-index" 1830 | checksum = "3e6cc1e89e689536eb5aeede61520e874df5a4707df811cd5da4aa5fbb2aae19" 1831 | dependencies = [ 1832 | "base64 0.22.0", 1833 | "bytes", 1834 | "encoding_rs", 1835 | "futures-core", 1836 | "futures-util", 1837 | "h2 0.4.4", 1838 | "http 1.1.0", 1839 | "http-body 1.0.0", 1840 | "http-body-util", 1841 | "hyper 1.2.0", 1842 | "hyper-rustls", 1843 | "hyper-tls", 1844 | "hyper-util", 1845 | "ipnet", 1846 | "js-sys", 1847 | "log", 1848 | "mime", 1849 | "native-tls", 1850 | "once_cell", 1851 | "percent-encoding", 1852 | "pin-project-lite", 1853 | "rustls", 1854 | "rustls-native-certs", 1855 | "rustls-pemfile", 1856 | "rustls-pki-types", 1857 | "serde", 1858 | "serde_json", 1859 | "serde_urlencoded", 1860 | "sync_wrapper", 1861 | "system-configuration", 1862 | "tokio", 1863 | "tokio-native-tls", 1864 | "tokio-rustls", 1865 | "tokio-util", 1866 | "tower-service", 1867 | "url", 1868 | "wasm-bindgen", 1869 | "wasm-bindgen-futures", 1870 | "wasm-streams", 1871 | "web-sys", 1872 | "winreg", 1873 | ] 1874 | 1875 | [[package]] 1876 | name = "reqwest-middleware" 1877 | version = "0.3.0" 1878 | source = "registry+https://github.com/rust-lang/crates.io-index" 1879 | checksum = "0209efb52486ad88136190094ee214759ef7507068b27992256ed6610eb71a01" 1880 | dependencies = [ 1881 | "anyhow", 1882 | "async-trait", 1883 | "http 1.1.0", 1884 | "reqwest", 1885 | "serde", 1886 | "thiserror", 1887 | "tower-service", 1888 | ] 1889 | 1890 | [[package]] 1891 | name = "reqwest-retry" 1892 | version = "0.5.0" 1893 | source = "registry+https://github.com/rust-lang/crates.io-index" 1894 | checksum = "40f342894422862af74c50e1e9601cf0931accc9c6981e5eb413c46603b616b5" 1895 | dependencies = [ 1896 | "anyhow", 1897 | "async-trait", 1898 | "chrono", 1899 | "futures", 1900 | "getrandom", 1901 | "http 1.1.0", 1902 | "hyper 1.2.0", 1903 | "parking_lot 0.11.2", 1904 | "reqwest", 1905 | "reqwest-middleware", 1906 | "retry-policies", 1907 | "tokio", 1908 | "tracing", 1909 | "wasm-timer", 1910 | ] 1911 | 1912 | [[package]] 1913 | name = "retry-policies" 1914 | version = "0.3.0" 1915 | source = "registry+https://github.com/rust-lang/crates.io-index" 1916 | checksum = "493b4243e32d6eedd29f9a398896e35c6943a123b55eec97dcaee98310d25810" 1917 | dependencies = [ 1918 | "anyhow", 1919 | "chrono", 1920 | "rand", 1921 | ] 1922 | 1923 | [[package]] 1924 | name = "ring" 1925 | version = "0.17.8" 1926 | source = "registry+https://github.com/rust-lang/crates.io-index" 1927 | checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" 1928 | dependencies = [ 1929 | "cc", 1930 | "cfg-if", 1931 | "getrandom", 1932 | "libc", 1933 | "spin", 1934 | "untrusted", 1935 | "windows-sys 0.52.0", 1936 | ] 1937 | 1938 | [[package]] 1939 | name = "rustc-demangle" 1940 | version = "0.1.23" 1941 | source = "registry+https://github.com/rust-lang/crates.io-index" 1942 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 1943 | 1944 | [[package]] 1945 | name = "rustc-hash" 1946 | version = "1.1.0" 1947 | source = "registry+https://github.com/rust-lang/crates.io-index" 1948 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 1949 | 1950 | [[package]] 1951 | name = "rustix" 1952 | version = "0.38.32" 1953 | source = "registry+https://github.com/rust-lang/crates.io-index" 1954 | checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" 1955 | dependencies = [ 1956 | "bitflags 2.5.0", 1957 | "errno", 1958 | "libc", 1959 | "linux-raw-sys", 1960 | "windows-sys 0.52.0", 1961 | ] 1962 | 1963 | [[package]] 1964 | name = "rustls" 1965 | version = "0.22.4" 1966 | source = "registry+https://github.com/rust-lang/crates.io-index" 1967 | checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" 1968 | dependencies = [ 1969 | "log", 1970 | "ring", 1971 | "rustls-pki-types", 1972 | "rustls-webpki", 1973 | "subtle", 1974 | "zeroize", 1975 | ] 1976 | 1977 | [[package]] 1978 | name = "rustls-native-certs" 1979 | version = "0.7.0" 1980 | source = "registry+https://github.com/rust-lang/crates.io-index" 1981 | checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" 1982 | dependencies = [ 1983 | "openssl-probe", 1984 | "rustls-pemfile", 1985 | "rustls-pki-types", 1986 | "schannel", 1987 | "security-framework", 1988 | ] 1989 | 1990 | [[package]] 1991 | name = "rustls-pemfile" 1992 | version = "2.1.2" 1993 | source = "registry+https://github.com/rust-lang/crates.io-index" 1994 | checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" 1995 | dependencies = [ 1996 | "base64 0.22.0", 1997 | "rustls-pki-types", 1998 | ] 1999 | 2000 | [[package]] 2001 | name = "rustls-pki-types" 2002 | version = "1.4.1" 2003 | source = "registry+https://github.com/rust-lang/crates.io-index" 2004 | checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" 2005 | 2006 | [[package]] 2007 | name = "rustls-webpki" 2008 | version = "0.102.3" 2009 | source = "registry+https://github.com/rust-lang/crates.io-index" 2010 | checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" 2011 | dependencies = [ 2012 | "ring", 2013 | "rustls-pki-types", 2014 | "untrusted", 2015 | ] 2016 | 2017 | [[package]] 2018 | name = "rustversion" 2019 | version = "1.0.15" 2020 | source = "registry+https://github.com/rust-lang/crates.io-index" 2021 | checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" 2022 | 2023 | [[package]] 2024 | name = "ryu" 2025 | version = "1.0.17" 2026 | source = "registry+https://github.com/rust-lang/crates.io-index" 2027 | checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" 2028 | 2029 | [[package]] 2030 | name = "same-file" 2031 | version = "1.0.6" 2032 | source = "registry+https://github.com/rust-lang/crates.io-index" 2033 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 2034 | dependencies = [ 2035 | "winapi-util", 2036 | ] 2037 | 2038 | [[package]] 2039 | name = "schannel" 2040 | version = "0.1.23" 2041 | source = "registry+https://github.com/rust-lang/crates.io-index" 2042 | checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" 2043 | dependencies = [ 2044 | "windows-sys 0.52.0", 2045 | ] 2046 | 2047 | [[package]] 2048 | name = "scopeguard" 2049 | version = "1.2.0" 2050 | source = "registry+https://github.com/rust-lang/crates.io-index" 2051 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 2052 | 2053 | [[package]] 2054 | name = "security-framework" 2055 | version = "2.10.0" 2056 | source = "registry+https://github.com/rust-lang/crates.io-index" 2057 | checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" 2058 | dependencies = [ 2059 | "bitflags 1.3.2", 2060 | "core-foundation", 2061 | "core-foundation-sys", 2062 | "libc", 2063 | "security-framework-sys", 2064 | ] 2065 | 2066 | [[package]] 2067 | name = "security-framework-sys" 2068 | version = "2.10.0" 2069 | source = "registry+https://github.com/rust-lang/crates.io-index" 2070 | checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" 2071 | dependencies = [ 2072 | "core-foundation-sys", 2073 | "libc", 2074 | ] 2075 | 2076 | [[package]] 2077 | name = "semver" 2078 | version = "1.0.22" 2079 | source = "registry+https://github.com/rust-lang/crates.io-index" 2080 | checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" 2081 | 2082 | [[package]] 2083 | name = "serde" 2084 | version = "1.0.197" 2085 | source = "registry+https://github.com/rust-lang/crates.io-index" 2086 | checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" 2087 | dependencies = [ 2088 | "serde_derive", 2089 | ] 2090 | 2091 | [[package]] 2092 | name = "serde_derive" 2093 | version = "1.0.197" 2094 | source = "registry+https://github.com/rust-lang/crates.io-index" 2095 | checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" 2096 | dependencies = [ 2097 | "proc-macro2", 2098 | "quote", 2099 | "syn 2.0.53", 2100 | ] 2101 | 2102 | [[package]] 2103 | name = "serde_json" 2104 | version = "1.0.115" 2105 | source = "registry+https://github.com/rust-lang/crates.io-index" 2106 | checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" 2107 | dependencies = [ 2108 | "itoa", 2109 | "ryu", 2110 | "serde", 2111 | ] 2112 | 2113 | [[package]] 2114 | name = "serde_urlencoded" 2115 | version = "0.7.1" 2116 | source = "registry+https://github.com/rust-lang/crates.io-index" 2117 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 2118 | dependencies = [ 2119 | "form_urlencoded", 2120 | "itoa", 2121 | "ryu", 2122 | "serde", 2123 | ] 2124 | 2125 | [[package]] 2126 | name = "sharded-slab" 2127 | version = "0.1.7" 2128 | source = "registry+https://github.com/rust-lang/crates.io-index" 2129 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 2130 | dependencies = [ 2131 | "lazy_static", 2132 | ] 2133 | 2134 | [[package]] 2135 | name = "shlex" 2136 | version = "1.3.0" 2137 | source = "registry+https://github.com/rust-lang/crates.io-index" 2138 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 2139 | 2140 | [[package]] 2141 | name = "signal-hook-registry" 2142 | version = "1.4.1" 2143 | source = "registry+https://github.com/rust-lang/crates.io-index" 2144 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 2145 | dependencies = [ 2146 | "libc", 2147 | ] 2148 | 2149 | [[package]] 2150 | name = "slab" 2151 | version = "0.4.9" 2152 | source = "registry+https://github.com/rust-lang/crates.io-index" 2153 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 2154 | dependencies = [ 2155 | "autocfg", 2156 | ] 2157 | 2158 | [[package]] 2159 | name = "smallvec" 2160 | version = "1.13.1" 2161 | source = "registry+https://github.com/rust-lang/crates.io-index" 2162 | checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" 2163 | 2164 | [[package]] 2165 | name = "snafu" 2166 | version = "0.7.5" 2167 | source = "registry+https://github.com/rust-lang/crates.io-index" 2168 | checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" 2169 | dependencies = [ 2170 | "doc-comment", 2171 | "snafu-derive", 2172 | ] 2173 | 2174 | [[package]] 2175 | name = "snafu-derive" 2176 | version = "0.7.5" 2177 | source = "registry+https://github.com/rust-lang/crates.io-index" 2178 | checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" 2179 | dependencies = [ 2180 | "heck 0.4.1", 2181 | "proc-macro2", 2182 | "quote", 2183 | "syn 1.0.109", 2184 | ] 2185 | 2186 | [[package]] 2187 | name = "socket2" 2188 | version = "0.5.6" 2189 | source = "registry+https://github.com/rust-lang/crates.io-index" 2190 | checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" 2191 | dependencies = [ 2192 | "libc", 2193 | "windows-sys 0.52.0", 2194 | ] 2195 | 2196 | [[package]] 2197 | name = "spin" 2198 | version = "0.9.8" 2199 | source = "registry+https://github.com/rust-lang/crates.io-index" 2200 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 2201 | 2202 | [[package]] 2203 | name = "strsim" 2204 | version = "0.10.0" 2205 | source = "registry+https://github.com/rust-lang/crates.io-index" 2206 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 2207 | 2208 | [[package]] 2209 | name = "strsim" 2210 | version = "0.11.1" 2211 | source = "registry+https://github.com/rust-lang/crates.io-index" 2212 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 2213 | 2214 | [[package]] 2215 | name = "subtle" 2216 | version = "2.5.0" 2217 | source = "registry+https://github.com/rust-lang/crates.io-index" 2218 | checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" 2219 | 2220 | [[package]] 2221 | name = "syn" 2222 | version = "1.0.109" 2223 | source = "registry+https://github.com/rust-lang/crates.io-index" 2224 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 2225 | dependencies = [ 2226 | "proc-macro2", 2227 | "quote", 2228 | "unicode-ident", 2229 | ] 2230 | 2231 | [[package]] 2232 | name = "syn" 2233 | version = "2.0.53" 2234 | source = "registry+https://github.com/rust-lang/crates.io-index" 2235 | checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" 2236 | dependencies = [ 2237 | "proc-macro2", 2238 | "quote", 2239 | "unicode-ident", 2240 | ] 2241 | 2242 | [[package]] 2243 | name = "sync_wrapper" 2244 | version = "0.1.2" 2245 | source = "registry+https://github.com/rust-lang/crates.io-index" 2246 | checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" 2247 | 2248 | [[package]] 2249 | name = "system-configuration" 2250 | version = "0.5.1" 2251 | source = "registry+https://github.com/rust-lang/crates.io-index" 2252 | checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" 2253 | dependencies = [ 2254 | "bitflags 1.3.2", 2255 | "core-foundation", 2256 | "system-configuration-sys", 2257 | ] 2258 | 2259 | [[package]] 2260 | name = "system-configuration-sys" 2261 | version = "0.5.0" 2262 | source = "registry+https://github.com/rust-lang/crates.io-index" 2263 | checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" 2264 | dependencies = [ 2265 | "core-foundation-sys", 2266 | "libc", 2267 | ] 2268 | 2269 | [[package]] 2270 | name = "tempfile" 2271 | version = "3.10.1" 2272 | source = "registry+https://github.com/rust-lang/crates.io-index" 2273 | checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" 2274 | dependencies = [ 2275 | "cfg-if", 2276 | "fastrand", 2277 | "rustix", 2278 | "windows-sys 0.52.0", 2279 | ] 2280 | 2281 | [[package]] 2282 | name = "thiserror" 2283 | version = "1.0.58" 2284 | source = "registry+https://github.com/rust-lang/crates.io-index" 2285 | checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" 2286 | dependencies = [ 2287 | "thiserror-impl", 2288 | ] 2289 | 2290 | [[package]] 2291 | name = "thiserror-impl" 2292 | version = "1.0.58" 2293 | source = "registry+https://github.com/rust-lang/crates.io-index" 2294 | checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" 2295 | dependencies = [ 2296 | "proc-macro2", 2297 | "quote", 2298 | "syn 2.0.53", 2299 | ] 2300 | 2301 | [[package]] 2302 | name = "thread_local" 2303 | version = "1.1.8" 2304 | source = "registry+https://github.com/rust-lang/crates.io-index" 2305 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 2306 | dependencies = [ 2307 | "cfg-if", 2308 | "once_cell", 2309 | ] 2310 | 2311 | [[package]] 2312 | name = "tinyvec" 2313 | version = "1.6.0" 2314 | source = "registry+https://github.com/rust-lang/crates.io-index" 2315 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 2316 | dependencies = [ 2317 | "tinyvec_macros", 2318 | ] 2319 | 2320 | [[package]] 2321 | name = "tinyvec_macros" 2322 | version = "0.1.1" 2323 | source = "registry+https://github.com/rust-lang/crates.io-index" 2324 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 2325 | 2326 | [[package]] 2327 | name = "tokio" 2328 | version = "1.36.0" 2329 | source = "registry+https://github.com/rust-lang/crates.io-index" 2330 | checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" 2331 | dependencies = [ 2332 | "backtrace", 2333 | "bytes", 2334 | "libc", 2335 | "mio", 2336 | "num_cpus", 2337 | "parking_lot 0.12.1", 2338 | "pin-project-lite", 2339 | "signal-hook-registry", 2340 | "socket2", 2341 | "tokio-macros", 2342 | "tracing", 2343 | "windows-sys 0.48.0", 2344 | ] 2345 | 2346 | [[package]] 2347 | name = "tokio-io-timeout" 2348 | version = "1.2.0" 2349 | source = "registry+https://github.com/rust-lang/crates.io-index" 2350 | checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" 2351 | dependencies = [ 2352 | "pin-project-lite", 2353 | "tokio", 2354 | ] 2355 | 2356 | [[package]] 2357 | name = "tokio-macros" 2358 | version = "2.2.0" 2359 | source = "registry+https://github.com/rust-lang/crates.io-index" 2360 | checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" 2361 | dependencies = [ 2362 | "proc-macro2", 2363 | "quote", 2364 | "syn 2.0.53", 2365 | ] 2366 | 2367 | [[package]] 2368 | name = "tokio-native-tls" 2369 | version = "0.3.1" 2370 | source = "registry+https://github.com/rust-lang/crates.io-index" 2371 | checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 2372 | dependencies = [ 2373 | "native-tls", 2374 | "tokio", 2375 | ] 2376 | 2377 | [[package]] 2378 | name = "tokio-rustls" 2379 | version = "0.25.0" 2380 | source = "registry+https://github.com/rust-lang/crates.io-index" 2381 | checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" 2382 | dependencies = [ 2383 | "rustls", 2384 | "rustls-pki-types", 2385 | "tokio", 2386 | ] 2387 | 2388 | [[package]] 2389 | name = "tokio-stream" 2390 | version = "0.1.15" 2391 | source = "registry+https://github.com/rust-lang/crates.io-index" 2392 | checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" 2393 | dependencies = [ 2394 | "futures-core", 2395 | "pin-project-lite", 2396 | "tokio", 2397 | ] 2398 | 2399 | [[package]] 2400 | name = "tokio-util" 2401 | version = "0.7.10" 2402 | source = "registry+https://github.com/rust-lang/crates.io-index" 2403 | checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" 2404 | dependencies = [ 2405 | "bytes", 2406 | "futures-core", 2407 | "futures-sink", 2408 | "pin-project-lite", 2409 | "tokio", 2410 | "tracing", 2411 | ] 2412 | 2413 | [[package]] 2414 | name = "tonic" 2415 | version = "0.10.2" 2416 | source = "registry+https://github.com/rust-lang/crates.io-index" 2417 | checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" 2418 | dependencies = [ 2419 | "async-stream", 2420 | "async-trait", 2421 | "axum", 2422 | "base64 0.21.7", 2423 | "bytes", 2424 | "h2 0.3.26", 2425 | "http 0.2.12", 2426 | "http-body 0.4.6", 2427 | "hyper 0.14.28", 2428 | "hyper-timeout", 2429 | "percent-encoding", 2430 | "pin-project", 2431 | "prost", 2432 | "tokio", 2433 | "tokio-stream", 2434 | "tower", 2435 | "tower-layer", 2436 | "tower-service", 2437 | "tracing", 2438 | ] 2439 | 2440 | [[package]] 2441 | name = "tower" 2442 | version = "0.4.13" 2443 | source = "registry+https://github.com/rust-lang/crates.io-index" 2444 | checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" 2445 | dependencies = [ 2446 | "futures-core", 2447 | "futures-util", 2448 | "indexmap 1.9.3", 2449 | "pin-project", 2450 | "pin-project-lite", 2451 | "rand", 2452 | "slab", 2453 | "tokio", 2454 | "tokio-util", 2455 | "tower-layer", 2456 | "tower-service", 2457 | "tracing", 2458 | ] 2459 | 2460 | [[package]] 2461 | name = "tower-layer" 2462 | version = "0.3.2" 2463 | source = "registry+https://github.com/rust-lang/crates.io-index" 2464 | checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" 2465 | 2466 | [[package]] 2467 | name = "tower-service" 2468 | version = "0.3.2" 2469 | source = "registry+https://github.com/rust-lang/crates.io-index" 2470 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 2471 | 2472 | [[package]] 2473 | name = "tracing" 2474 | version = "0.1.40" 2475 | source = "registry+https://github.com/rust-lang/crates.io-index" 2476 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 2477 | dependencies = [ 2478 | "log", 2479 | "pin-project-lite", 2480 | "tracing-attributes", 2481 | "tracing-core", 2482 | ] 2483 | 2484 | [[package]] 2485 | name = "tracing-attributes" 2486 | version = "0.1.27" 2487 | source = "registry+https://github.com/rust-lang/crates.io-index" 2488 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 2489 | dependencies = [ 2490 | "proc-macro2", 2491 | "quote", 2492 | "syn 2.0.53", 2493 | ] 2494 | 2495 | [[package]] 2496 | name = "tracing-core" 2497 | version = "0.1.32" 2498 | source = "registry+https://github.com/rust-lang/crates.io-index" 2499 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 2500 | dependencies = [ 2501 | "once_cell", 2502 | "valuable", 2503 | ] 2504 | 2505 | [[package]] 2506 | name = "tracing-subscriber" 2507 | version = "0.3.18" 2508 | source = "registry+https://github.com/rust-lang/crates.io-index" 2509 | checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" 2510 | dependencies = [ 2511 | "matchers", 2512 | "once_cell", 2513 | "regex", 2514 | "sharded-slab", 2515 | "thread_local", 2516 | "tracing", 2517 | "tracing-core", 2518 | ] 2519 | 2520 | [[package]] 2521 | name = "try-lock" 2522 | version = "0.2.5" 2523 | source = "registry+https://github.com/rust-lang/crates.io-index" 2524 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 2525 | 2526 | [[package]] 2527 | name = "typenum" 2528 | version = "1.17.0" 2529 | source = "registry+https://github.com/rust-lang/crates.io-index" 2530 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 2531 | 2532 | [[package]] 2533 | name = "unicode-bidi" 2534 | version = "0.3.15" 2535 | source = "registry+https://github.com/rust-lang/crates.io-index" 2536 | checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 2537 | 2538 | [[package]] 2539 | name = "unicode-ident" 2540 | version = "1.0.12" 2541 | source = "registry+https://github.com/rust-lang/crates.io-index" 2542 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 2543 | 2544 | [[package]] 2545 | name = "unicode-normalization" 2546 | version = "0.1.23" 2547 | source = "registry+https://github.com/rust-lang/crates.io-index" 2548 | checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" 2549 | dependencies = [ 2550 | "tinyvec", 2551 | ] 2552 | 2553 | [[package]] 2554 | name = "untrusted" 2555 | version = "0.9.0" 2556 | source = "registry+https://github.com/rust-lang/crates.io-index" 2557 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 2558 | 2559 | [[package]] 2560 | name = "url" 2561 | version = "2.5.2" 2562 | source = "registry+https://github.com/rust-lang/crates.io-index" 2563 | checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" 2564 | dependencies = [ 2565 | "form_urlencoded", 2566 | "idna", 2567 | "percent-encoding", 2568 | ] 2569 | 2570 | [[package]] 2571 | name = "utf8parse" 2572 | version = "0.2.2" 2573 | source = "registry+https://github.com/rust-lang/crates.io-index" 2574 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 2575 | 2576 | [[package]] 2577 | name = "valuable" 2578 | version = "0.1.0" 2579 | source = "registry+https://github.com/rust-lang/crates.io-index" 2580 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 2581 | 2582 | [[package]] 2583 | name = "vcpkg" 2584 | version = "0.2.15" 2585 | source = "registry+https://github.com/rust-lang/crates.io-index" 2586 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 2587 | 2588 | [[package]] 2589 | name = "version_check" 2590 | version = "0.9.4" 2591 | source = "registry+https://github.com/rust-lang/crates.io-index" 2592 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 2593 | 2594 | [[package]] 2595 | name = "walkdir" 2596 | version = "2.5.0" 2597 | source = "registry+https://github.com/rust-lang/crates.io-index" 2598 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 2599 | dependencies = [ 2600 | "same-file", 2601 | "winapi-util", 2602 | ] 2603 | 2604 | [[package]] 2605 | name = "want" 2606 | version = "0.3.1" 2607 | source = "registry+https://github.com/rust-lang/crates.io-index" 2608 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 2609 | dependencies = [ 2610 | "try-lock", 2611 | ] 2612 | 2613 | [[package]] 2614 | name = "wasi" 2615 | version = "0.11.0+wasi-snapshot-preview1" 2616 | source = "registry+https://github.com/rust-lang/crates.io-index" 2617 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2618 | 2619 | [[package]] 2620 | name = "wasm-bindgen" 2621 | version = "0.2.92" 2622 | source = "registry+https://github.com/rust-lang/crates.io-index" 2623 | checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" 2624 | dependencies = [ 2625 | "cfg-if", 2626 | "wasm-bindgen-macro", 2627 | ] 2628 | 2629 | [[package]] 2630 | name = "wasm-bindgen-backend" 2631 | version = "0.2.92" 2632 | source = "registry+https://github.com/rust-lang/crates.io-index" 2633 | checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" 2634 | dependencies = [ 2635 | "bumpalo", 2636 | "log", 2637 | "once_cell", 2638 | "proc-macro2", 2639 | "quote", 2640 | "syn 2.0.53", 2641 | "wasm-bindgen-shared", 2642 | ] 2643 | 2644 | [[package]] 2645 | name = "wasm-bindgen-futures" 2646 | version = "0.4.42" 2647 | source = "registry+https://github.com/rust-lang/crates.io-index" 2648 | checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" 2649 | dependencies = [ 2650 | "cfg-if", 2651 | "js-sys", 2652 | "wasm-bindgen", 2653 | "web-sys", 2654 | ] 2655 | 2656 | [[package]] 2657 | name = "wasm-bindgen-macro" 2658 | version = "0.2.92" 2659 | source = "registry+https://github.com/rust-lang/crates.io-index" 2660 | checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" 2661 | dependencies = [ 2662 | "quote", 2663 | "wasm-bindgen-macro-support", 2664 | ] 2665 | 2666 | [[package]] 2667 | name = "wasm-bindgen-macro-support" 2668 | version = "0.2.92" 2669 | source = "registry+https://github.com/rust-lang/crates.io-index" 2670 | checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" 2671 | dependencies = [ 2672 | "proc-macro2", 2673 | "quote", 2674 | "syn 2.0.53", 2675 | "wasm-bindgen-backend", 2676 | "wasm-bindgen-shared", 2677 | ] 2678 | 2679 | [[package]] 2680 | name = "wasm-bindgen-shared" 2681 | version = "0.2.92" 2682 | source = "registry+https://github.com/rust-lang/crates.io-index" 2683 | checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" 2684 | 2685 | [[package]] 2686 | name = "wasm-streams" 2687 | version = "0.4.0" 2688 | source = "registry+https://github.com/rust-lang/crates.io-index" 2689 | checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" 2690 | dependencies = [ 2691 | "futures-util", 2692 | "js-sys", 2693 | "wasm-bindgen", 2694 | "wasm-bindgen-futures", 2695 | "web-sys", 2696 | ] 2697 | 2698 | [[package]] 2699 | name = "wasm-timer" 2700 | version = "0.2.5" 2701 | source = "registry+https://github.com/rust-lang/crates.io-index" 2702 | checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" 2703 | dependencies = [ 2704 | "futures", 2705 | "js-sys", 2706 | "parking_lot 0.11.2", 2707 | "pin-utils", 2708 | "wasm-bindgen", 2709 | "wasm-bindgen-futures", 2710 | "web-sys", 2711 | ] 2712 | 2713 | [[package]] 2714 | name = "web-sys" 2715 | version = "0.3.69" 2716 | source = "registry+https://github.com/rust-lang/crates.io-index" 2717 | checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" 2718 | dependencies = [ 2719 | "js-sys", 2720 | "wasm-bindgen", 2721 | ] 2722 | 2723 | [[package]] 2724 | name = "which" 2725 | version = "4.4.2" 2726 | source = "registry+https://github.com/rust-lang/crates.io-index" 2727 | checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" 2728 | dependencies = [ 2729 | "either", 2730 | "home", 2731 | "once_cell", 2732 | "rustix", 2733 | ] 2734 | 2735 | [[package]] 2736 | name = "winapi" 2737 | version = "0.3.9" 2738 | source = "registry+https://github.com/rust-lang/crates.io-index" 2739 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2740 | dependencies = [ 2741 | "winapi-i686-pc-windows-gnu", 2742 | "winapi-x86_64-pc-windows-gnu", 2743 | ] 2744 | 2745 | [[package]] 2746 | name = "winapi-i686-pc-windows-gnu" 2747 | version = "0.4.0" 2748 | source = "registry+https://github.com/rust-lang/crates.io-index" 2749 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2750 | 2751 | [[package]] 2752 | name = "winapi-util" 2753 | version = "0.1.6" 2754 | source = "registry+https://github.com/rust-lang/crates.io-index" 2755 | checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" 2756 | dependencies = [ 2757 | "winapi", 2758 | ] 2759 | 2760 | [[package]] 2761 | name = "winapi-x86_64-pc-windows-gnu" 2762 | version = "0.4.0" 2763 | source = "registry+https://github.com/rust-lang/crates.io-index" 2764 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2765 | 2766 | [[package]] 2767 | name = "windows-core" 2768 | version = "0.52.0" 2769 | source = "registry+https://github.com/rust-lang/crates.io-index" 2770 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 2771 | dependencies = [ 2772 | "windows-targets 0.52.4", 2773 | ] 2774 | 2775 | [[package]] 2776 | name = "windows-sys" 2777 | version = "0.48.0" 2778 | source = "registry+https://github.com/rust-lang/crates.io-index" 2779 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 2780 | dependencies = [ 2781 | "windows-targets 0.48.5", 2782 | ] 2783 | 2784 | [[package]] 2785 | name = "windows-sys" 2786 | version = "0.52.0" 2787 | source = "registry+https://github.com/rust-lang/crates.io-index" 2788 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2789 | dependencies = [ 2790 | "windows-targets 0.52.4", 2791 | ] 2792 | 2793 | [[package]] 2794 | name = "windows-targets" 2795 | version = "0.48.5" 2796 | source = "registry+https://github.com/rust-lang/crates.io-index" 2797 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 2798 | dependencies = [ 2799 | "windows_aarch64_gnullvm 0.48.5", 2800 | "windows_aarch64_msvc 0.48.5", 2801 | "windows_i686_gnu 0.48.5", 2802 | "windows_i686_msvc 0.48.5", 2803 | "windows_x86_64_gnu 0.48.5", 2804 | "windows_x86_64_gnullvm 0.48.5", 2805 | "windows_x86_64_msvc 0.48.5", 2806 | ] 2807 | 2808 | [[package]] 2809 | name = "windows-targets" 2810 | version = "0.52.4" 2811 | source = "registry+https://github.com/rust-lang/crates.io-index" 2812 | checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" 2813 | dependencies = [ 2814 | "windows_aarch64_gnullvm 0.52.4", 2815 | "windows_aarch64_msvc 0.52.4", 2816 | "windows_i686_gnu 0.52.4", 2817 | "windows_i686_msvc 0.52.4", 2818 | "windows_x86_64_gnu 0.52.4", 2819 | "windows_x86_64_gnullvm 0.52.4", 2820 | "windows_x86_64_msvc 0.52.4", 2821 | ] 2822 | 2823 | [[package]] 2824 | name = "windows_aarch64_gnullvm" 2825 | version = "0.48.5" 2826 | source = "registry+https://github.com/rust-lang/crates.io-index" 2827 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2828 | 2829 | [[package]] 2830 | name = "windows_aarch64_gnullvm" 2831 | version = "0.52.4" 2832 | source = "registry+https://github.com/rust-lang/crates.io-index" 2833 | checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" 2834 | 2835 | [[package]] 2836 | name = "windows_aarch64_msvc" 2837 | version = "0.48.5" 2838 | source = "registry+https://github.com/rust-lang/crates.io-index" 2839 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2840 | 2841 | [[package]] 2842 | name = "windows_aarch64_msvc" 2843 | version = "0.52.4" 2844 | source = "registry+https://github.com/rust-lang/crates.io-index" 2845 | checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" 2846 | 2847 | [[package]] 2848 | name = "windows_i686_gnu" 2849 | version = "0.48.5" 2850 | source = "registry+https://github.com/rust-lang/crates.io-index" 2851 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 2852 | 2853 | [[package]] 2854 | name = "windows_i686_gnu" 2855 | version = "0.52.4" 2856 | source = "registry+https://github.com/rust-lang/crates.io-index" 2857 | checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" 2858 | 2859 | [[package]] 2860 | name = "windows_i686_msvc" 2861 | version = "0.48.5" 2862 | source = "registry+https://github.com/rust-lang/crates.io-index" 2863 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2864 | 2865 | [[package]] 2866 | name = "windows_i686_msvc" 2867 | version = "0.52.4" 2868 | source = "registry+https://github.com/rust-lang/crates.io-index" 2869 | checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" 2870 | 2871 | [[package]] 2872 | name = "windows_x86_64_gnu" 2873 | version = "0.48.5" 2874 | source = "registry+https://github.com/rust-lang/crates.io-index" 2875 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2876 | 2877 | [[package]] 2878 | name = "windows_x86_64_gnu" 2879 | version = "0.52.4" 2880 | source = "registry+https://github.com/rust-lang/crates.io-index" 2881 | checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" 2882 | 2883 | [[package]] 2884 | name = "windows_x86_64_gnullvm" 2885 | version = "0.48.5" 2886 | source = "registry+https://github.com/rust-lang/crates.io-index" 2887 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2888 | 2889 | [[package]] 2890 | name = "windows_x86_64_gnullvm" 2891 | version = "0.52.4" 2892 | source = "registry+https://github.com/rust-lang/crates.io-index" 2893 | checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" 2894 | 2895 | [[package]] 2896 | name = "windows_x86_64_msvc" 2897 | version = "0.48.5" 2898 | source = "registry+https://github.com/rust-lang/crates.io-index" 2899 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2900 | 2901 | [[package]] 2902 | name = "windows_x86_64_msvc" 2903 | version = "0.52.4" 2904 | source = "registry+https://github.com/rust-lang/crates.io-index" 2905 | checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" 2906 | 2907 | [[package]] 2908 | name = "winreg" 2909 | version = "0.52.0" 2910 | source = "registry+https://github.com/rust-lang/crates.io-index" 2911 | checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" 2912 | dependencies = [ 2913 | "cfg-if", 2914 | "windows-sys 0.48.0", 2915 | ] 2916 | 2917 | [[package]] 2918 | name = "zerocopy" 2919 | version = "0.7.32" 2920 | source = "registry+https://github.com/rust-lang/crates.io-index" 2921 | checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" 2922 | dependencies = [ 2923 | "zerocopy-derive", 2924 | ] 2925 | 2926 | [[package]] 2927 | name = "zerocopy-derive" 2928 | version = "0.7.32" 2929 | source = "registry+https://github.com/rust-lang/crates.io-index" 2930 | checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" 2931 | dependencies = [ 2932 | "proc-macro2", 2933 | "quote", 2934 | "syn 2.0.53", 2935 | ] 2936 | 2937 | [[package]] 2938 | name = "zeroize" 2939 | version = "1.7.0" 2940 | source = "registry+https://github.com/rust-lang/crates.io-index" 2941 | checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" 2942 | --------------------------------------------------------------------------------