chore: praise be the clippy gods
This commit is contained in:
parent
fe4a47d6cc
commit
e4dcb48878
9 changed files with 55 additions and 90 deletions
|
@ -1,6 +1,3 @@
|
||||||
use std::{env, fs};
|
|
||||||
use std::hint::black_box;
|
|
||||||
use std::time::Instant;
|
|
||||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||||
use wxbox_ar2::parse;
|
use wxbox_ar2::parse;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![warn(clippy::all)]
|
||||||
|
|
||||||
use nexrad_data::volume::File;
|
use nexrad_data::volume::File;
|
||||||
use nexrad_decode::messages::MessageContents;
|
use nexrad_decode::messages::MessageContents;
|
||||||
use nexrad_decode::messages::digital_radar_data::{GenericDataBlock, RadialStatus};
|
use nexrad_decode::messages::digital_radar_data::{GenericDataBlock, RadialStatus};
|
||||||
|
@ -46,7 +48,6 @@ pub struct MomentData {
|
||||||
|
|
||||||
impl MomentData {
|
impl MomentData {
|
||||||
/// Values from this data moment corresponding to gates in the radial.
|
/// Values from this data moment corresponding to gates in the radial.
|
||||||
|
|
||||||
pub fn values(&self) -> Vec<MomentValue> {
|
pub fn values(&self) -> Vec<MomentValue> {
|
||||||
let copied_values = self.values.iter().copied();
|
let copied_values = self.values.iter().copied();
|
||||||
|
|
||||||
|
@ -67,9 +68,7 @@ impl MomentData {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The data moment value for a product in a radial's gate. The value may be a floating-point number
|
/// The data moment value for a product in a radial's gate. The value may be a floating-point number
|
||||||
|
|
||||||
/// or a special case such as "below threshold" or "range folded".
|
/// or a special case such as "below threshold" or "range folded".
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
|
||||||
pub enum MomentValue {
|
pub enum MomentValue {
|
||||||
|
@ -89,7 +88,7 @@ pub fn parse(input: Vec<u8>) -> nexrad_data::result::Result<Scan> {
|
||||||
let mut vcp: Option<u16> = None;
|
let mut vcp: Option<u16> = None;
|
||||||
let mut radials = vec![];
|
let mut radials = vec![];
|
||||||
|
|
||||||
let mut radial_chunks: Vec<nexrad_data::result::Result<(Vec<Radial>, Option<u16>)>> = file.records()
|
let radial_chunks: Vec<nexrad_data::result::Result<(Vec<Radial>, Option<u16>)>> = file.records()
|
||||||
.par_iter_mut()
|
.par_iter_mut()
|
||||||
.map(|record| {
|
.map(|record| {
|
||||||
let mut vcp = None;
|
let mut vcp = None;
|
||||||
|
@ -145,23 +144,23 @@ fn into_radial(
|
||||||
radial_status: message.header.radial_status(),
|
radial_status: message.header.radial_status(),
|
||||||
elevation_number: message.header.elevation_number,
|
elevation_number: message.header.elevation_number,
|
||||||
elevation_number_degrees: message.header.elevation_angle,
|
elevation_number_degrees: message.header.elevation_angle,
|
||||||
reflectivity: message.reflectivity_data_block.map(|u| into_moment_data(u)),
|
reflectivity: message.reflectivity_data_block.map(into_moment_data),
|
||||||
velocity: message.velocity_data_block.map(|u| into_moment_data(u)),
|
velocity: message.velocity_data_block.map(into_moment_data),
|
||||||
spectrum_width: message
|
spectrum_width: message
|
||||||
.spectrum_width_data_block
|
.spectrum_width_data_block
|
||||||
.map(|u| into_moment_data(u)),
|
.map(into_moment_data),
|
||||||
differential_reflectivity: message
|
differential_reflectivity: message
|
||||||
.differential_reflectivity_data_block
|
.differential_reflectivity_data_block
|
||||||
.map(|u| into_moment_data(u)),
|
.map(into_moment_data),
|
||||||
differential_phase: message
|
differential_phase: message
|
||||||
.differential_phase_data_block
|
.differential_phase_data_block
|
||||||
.map(|u| into_moment_data(u)),
|
.map(into_moment_data),
|
||||||
correlation_coefficient: message
|
correlation_coefficient: message
|
||||||
.correlation_coefficient_data_block
|
.correlation_coefficient_data_block
|
||||||
.map(|u| into_moment_data(u)),
|
.map(into_moment_data),
|
||||||
specific_differential_phase: message
|
specific_differential_phase: message
|
||||||
.specific_diff_phase_data_block
|
.specific_diff_phase_data_block
|
||||||
.map(|u| into_moment_data(u)),
|
.map(into_moment_data),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
use std::fs::File;
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use wxbox_ar2::parse;
|
use wxbox_ar2::parse;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{LazyLock, OnceLock};
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct Wsr88dSite {
|
pub struct Wsr88dSite {
|
||||||
|
@ -30,7 +30,7 @@ where
|
||||||
{
|
{
|
||||||
let s: String = Deserialize::deserialize(deserializer)?;
|
let s: String = Deserialize::deserialize(deserializer)?;
|
||||||
|
|
||||||
let elevation = s.split(" ").nth(0).unwrap();
|
let elevation = s.split(" ").next().unwrap();
|
||||||
Ok(elevation.parse().unwrap())
|
Ok(elevation.parse().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
|
|
||||||
use std::{env, fs};
|
|
||||||
use std::hint::black_box;
|
|
||||||
use std::time::Instant;
|
|
||||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||||
use wxbox_grib2::GribMessage;
|
use wxbox_grib2::GribMessage;
|
||||||
|
|
||||||
|
|
|
@ -2,28 +2,24 @@ use crate::AppState;
|
||||||
use crate::error::AppError;
|
use crate::error::AppError;
|
||||||
use crate::tiles::{DataId, TileId};
|
use crate::tiles::{DataId, TileId};
|
||||||
use anyhow::{anyhow, bail};
|
use anyhow::{anyhow, bail};
|
||||||
|
use axum::Json;
|
||||||
use axum::extract::{Path, State};
|
use axum::extract::{Path, State};
|
||||||
use axum::http::{StatusCode, header};
|
use axum::http::header;
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
use flate2::read::GzDecoder;
|
use flate2::read::GzDecoder;
|
||||||
use image::codecs::png::PngEncoder;
|
use image::codecs::png::PngEncoder;
|
||||||
use image::{Rgba, RgbaImage};
|
use image::{Rgba, RgbaImage};
|
||||||
use moka::future::Cache;
|
use moka::future::Cache;
|
||||||
use rayon::iter::IntoParallelIterator;
|
use rayon::iter::IntoParallelIterator;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::f64::consts::PI;
|
use std::f64::consts::PI;
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{Cursor, ErrorKind};
|
use std::io::{Cursor, ErrorKind};
|
||||||
use std::num::TryFromIntError;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use axum::Json;
|
use tracing::info_span;
|
||||||
use tokio::io::AsyncReadExt;
|
|
||||||
use tracing::{debug, info_span};
|
|
||||||
use wxbox_grib2::GribMessage;
|
use wxbox_grib2::GribMessage;
|
||||||
use wxbox_grib2::wgs84::LatLong;
|
use wxbox_grib2::wgs84::LatLong;
|
||||||
use wxbox_pal::{ColorPalette, Palette};
|
use wxbox_pal::ColorPalette;
|
||||||
|
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
use wxbox_common::{Grib2DataSource, GribMessageTimeMetadata, GribTileMetadata};
|
use wxbox_common::{Grib2DataSource, GribMessageTimeMetadata, GribTileMetadata};
|
||||||
|
@ -35,13 +31,11 @@ pub type Grib2DataConfig = HashMap<String, Grib2DataSource>;
|
||||||
|
|
||||||
#[tracing::instrument(level = "info")]
|
#[tracing::instrument(level = "info")]
|
||||||
pub async fn grib2_metadata(
|
pub async fn grib2_metadata(
|
||||||
Path((source)): Path<(String)>,
|
Path(source): Path<String>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> Result<Json<wxbox_common::GribTileMetadata>, AppError> {
|
) -> Result<Json<wxbox_common::GribTileMetadata>, AppError> {
|
||||||
// is this even a valid data source?
|
// is this even a valid data source?
|
||||||
let data_id = DataId {
|
let data_id = DataId { source };
|
||||||
source,
|
|
||||||
};
|
|
||||||
let Some(ds) = state.config.data.grib2.get(&data_id.source) else {
|
let Some(ds) = state.config.data.grib2.get(&data_id.source) else {
|
||||||
return Err(anyhow!("invalid/unknown grib2 state").into());
|
return Err(anyhow!("invalid/unknown grib2 state").into());
|
||||||
};
|
};
|
||||||
|
@ -185,10 +179,7 @@ async fn render_to_png(
|
||||||
|
|
||||||
let nearest_value = data.value_for(LatLong { lat, long }).map(|u| u as f64);
|
let nearest_value = data.value_for(LatLong { lat, long }).map(|u| u as f64);
|
||||||
|
|
||||||
let color =
|
colorize(nearest_value, &data_source).unwrap_or(Rgba::from([0, 0, 0, 0]))
|
||||||
colorize(nearest_value, &data_source).unwrap_or(Rgba::from([0, 0, 0, 0]));
|
|
||||||
|
|
||||||
color
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,25 +2,22 @@ mod config;
|
||||||
mod error;
|
mod error;
|
||||||
mod grib2;
|
mod grib2;
|
||||||
mod nexrad;
|
mod nexrad;
|
||||||
mod tiles;
|
|
||||||
mod nexrad_list_response;
|
mod nexrad_list_response;
|
||||||
|
mod tiles;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::grib2::{Grib2DataCache, Grib2TileCache, grib2_handler, grib2_metadata};
|
use crate::grib2::{Grib2DataCache, Grib2TileCache, grib2_handler, grib2_metadata};
|
||||||
use crate::nexrad::{NexradDataCache, NexradTileCache, nexrad_handler};
|
use crate::nexrad::{NexradDataCache, NexradTileCache, nexrad_handler};
|
||||||
use crate::tiles::{DataId, TileId};
|
|
||||||
use axum::Router;
|
use axum::Router;
|
||||||
|
use axum::http::Method;
|
||||||
use axum::routing::get;
|
use axum::routing::get;
|
||||||
use moka::future::Cache;
|
use moka::future::Cache;
|
||||||
use std::env::args;
|
use std::env::args;
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use axum::http::Method;
|
|
||||||
use tower_http::cors::{Any, CorsLayer};
|
use tower_http::cors::{Any, CorsLayer};
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
use tracing_subscriber::fmt::format::FmtSpan;
|
use tracing_subscriber::fmt::format::FmtSpan;
|
||||||
use tracing_subscriber::util::SubscriberInitExt;
|
|
||||||
use wxbox_grib2::GribMessage;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
use crate::error::AppError;
|
use crate::error::AppError;
|
||||||
|
use crate::nexrad_list_response::ListBucketResult;
|
||||||
use crate::tiles::{DataId, TileId};
|
use crate::tiles::{DataId, TileId};
|
||||||
use anyhow::{anyhow, bail};
|
use anyhow::{anyhow, bail};
|
||||||
use axum::extract::{Path, State};
|
use axum::extract::{Path, State};
|
||||||
use axum::http::{StatusCode, header};
|
use axum::http::header;
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
use flate2::read::GzDecoder;
|
use chrono::{Datelike, Utc};
|
||||||
use geographiclib::Geodesic;
|
use geographiclib::Geodesic;
|
||||||
use image::codecs::png::PngEncoder;
|
use image::codecs::png::PngEncoder;
|
||||||
use image::{Rgba, RgbaImage};
|
use image::{Rgba, RgbaImage};
|
||||||
|
@ -16,19 +17,13 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::f64::consts::PI;
|
use std::f64::consts::PI;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::{fs, io};
|
use std::io;
|
||||||
use std::io::{Cursor, ErrorKind};
|
use std::io::ErrorKind;
|
||||||
use std::num::TryFromIntError;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use chrono::{Datelike, Utc};
|
|
||||||
use tokio::io::AsyncReadExt;
|
|
||||||
use tracing::{debug, info_span};
|
use tracing::{debug, info_span};
|
||||||
use wxbox_ar2::sites::wsr88d::{SITES, Wsr88dSite};
|
use wxbox_ar2::sites::wsr88d::{SITES, Wsr88dSite};
|
||||||
use wxbox_ar2::{MomentValue, Radial, Scan, Sweep, parse};
|
use wxbox_ar2::{MomentValue, Radial, Scan, Sweep, parse};
|
||||||
use wxbox_grib2::GribMessage;
|
use wxbox_pal::ColorPalette;
|
||||||
use wxbox_grib2::wgs84::LatLong;
|
|
||||||
use wxbox_pal::{ColorPalette, Palette};
|
|
||||||
use crate::nexrad_list_response::ListBucketResult;
|
|
||||||
|
|
||||||
pub type NexradDataCache = Cache<DataId, Arc<wxbox_ar2::Scan>>;
|
pub type NexradDataCache = Cache<DataId, Arc<wxbox_ar2::Scan>>;
|
||||||
pub type NexradTileCache = Cache<TileId, Arc<Vec<u8>>>;
|
pub type NexradTileCache = Cache<TileId, Arc<Vec<u8>>>;
|
||||||
|
@ -92,8 +87,14 @@ pub async fn nexrad_handler(
|
||||||
|
|
||||||
// we know we need to build the tile, so let's do that now
|
// we know we need to build the tile, so let's do that now
|
||||||
// it also returns it, so we can conveniently return it right now
|
// it also returns it, so we can conveniently return it right now
|
||||||
let pixel_data =
|
let pixel_data = render_to_png(
|
||||||
render_to_png(state.nexrad_tile_cache.clone(), data, tile_id, &site, ds.clone()).await?;
|
state.nexrad_tile_cache.clone(),
|
||||||
|
data,
|
||||||
|
tile_id,
|
||||||
|
&site,
|
||||||
|
ds.clone(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
[(header::CONTENT_TYPE, "image/png")],
|
[(header::CONTENT_TYPE, "image/png")],
|
||||||
|
@ -104,23 +105,24 @@ pub async fn nexrad_handler(
|
||||||
async fn load_nexrad_data(
|
async fn load_nexrad_data(
|
||||||
cache: NexradDataCache,
|
cache: NexradDataCache,
|
||||||
data_id: DataId,
|
data_id: DataId,
|
||||||
data_source: NexradDataSource,
|
_data_source: NexradDataSource,
|
||||||
site: &str,
|
site: &str,
|
||||||
) -> anyhow::Result<Arc<Scan>> {
|
) -> anyhow::Result<Arc<Scan>> {
|
||||||
let _load_span = info_span!("load_nexrad_data");
|
let _load_span = info_span!("load_nexrad_data");
|
||||||
|
|
||||||
let mut searchdate = Utc::now();
|
let mut searchdate = Utc::now();
|
||||||
|
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let mut url;
|
let url;
|
||||||
let mut days_went_back = 0;
|
let mut days_went_back = 0;
|
||||||
|
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
let year = searchdate.year();
|
let year = searchdate.year();
|
||||||
let month = searchdate.month();
|
let month = searchdate.month();
|
||||||
let day = searchdate.day();
|
let day = searchdate.day();
|
||||||
let listing_url = format!("https://noaa-nexrad-level2.s3.amazonaws.com/?list-type=2&delimiter=/&prefix={year}/{month:02}/{day:02}/{site}/");
|
let listing_url = format!(
|
||||||
|
"https://noaa-nexrad-level2.s3.amazonaws.com/?list-type=2&delimiter=/&prefix={year}/{month:02}/{day:02}/{site}/"
|
||||||
|
);
|
||||||
debug!("downloading listing from {}", listing_url);
|
debug!("downloading listing from {}", listing_url);
|
||||||
let r = client.get(listing_url).send().await?;
|
let r = client.get(listing_url).send().await?;
|
||||||
if !r.status().is_success() {
|
if !r.status().is_success() {
|
||||||
|
@ -135,18 +137,20 @@ async fn load_nexrad_data(
|
||||||
// fake. continue
|
// fake. continue
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
debug!("nexrad: found l2 file for {} date {}", site, file.LastModified);
|
debug!(
|
||||||
|
"nexrad: found l2 file for {} date {}",
|
||||||
|
site, file.LastModified
|
||||||
|
);
|
||||||
url = format!("https://noaa-nexrad-level2.s3.amazonaws.com/{}", file.Key);
|
url = format!("https://noaa-nexrad-level2.s3.amazonaws.com/{}", file.Key);
|
||||||
break 'outer;
|
break 'outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
searchdate -= chrono::Duration::days(1);
|
searchdate -= chrono::Duration::days(1);
|
||||||
days_went_back += 1;
|
days_went_back += 1;
|
||||||
if days_went_back > 7 {
|
if days_went_back > 7 {
|
||||||
bail!("no recent radar data");
|
bail!("no recent radar data");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
|
@ -169,15 +173,6 @@ async fn load_nexrad_data(
|
||||||
const TWO_PI: f64 = PI * 2.0;
|
const TWO_PI: f64 = PI * 2.0;
|
||||||
const HALF_PI: f64 = PI / 2.0;
|
const HALF_PI: f64 = PI / 2.0;
|
||||||
|
|
||||||
const A: f64 = 6_378.1370; // km
|
|
||||||
const B: f64 = 6_356.7523; // km
|
|
||||||
|
|
||||||
fn radius_at_latitude(phi: f64) -> f64 {
|
|
||||||
let top = (A.powi(2) * phi.cos()).powi(2) + (B.powi(2) * phi.sin()).powi(2);
|
|
||||||
let bottom = (A * phi.cos()).powi(2) + (B * phi.sin()).powi(2);
|
|
||||||
(top / bottom).sqrt()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calculate_inverse(
|
fn calculate_inverse(
|
||||||
tile_x_times_tilesize: f64,
|
tile_x_times_tilesize: f64,
|
||||||
tile_y_times_tilesize: f64,
|
tile_y_times_tilesize: f64,
|
||||||
|
@ -241,7 +236,7 @@ async fn render_to_png(
|
||||||
let tile_x_times_tilesize = tile_id.x as f64 * tile_id.size as f64;
|
let tile_x_times_tilesize = tile_id.x as f64 * tile_id.size as f64;
|
||||||
let tile_y_times_tilesize = tile_id.y as f64 * tile_id.size as f64;
|
let tile_y_times_tilesize = tile_id.y as f64 * tile_id.size as f64;
|
||||||
|
|
||||||
let first_sweep = data.sweeps.get(0).unwrap();
|
let first_sweep = data.sweeps.first().unwrap();
|
||||||
let g = Geodesic::wgs84();
|
let g = Geodesic::wgs84();
|
||||||
|
|
||||||
let radar = SITES.sites.get(site).unwrap();
|
let radar = SITES.sites.get(site).unwrap();
|
||||||
|
@ -284,7 +279,7 @@ async fn render_to_png(
|
||||||
|
|
||||||
let data = &reflectivity.values;
|
let data = &reflectivity.values;
|
||||||
|
|
||||||
if gate > data.len() || gate < 0 {
|
if gate > data.len() {
|
||||||
return colorize(None, &data_source).unwrap_or(Rgba::from([0, 0, 0, 0]));
|
return colorize(None, &data_source).unwrap_or(Rgba::from([0, 0, 0, 0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,7 +291,7 @@ async fn render_to_png(
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
return colorize(color, &data_source).unwrap_or(Rgba::from([0, 0, 0, 0]));
|
colorize(color, &data_source).unwrap_or(Rgba::from([0, 0, 0, 0]))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
|
@ -338,12 +333,7 @@ fn colorize(
|
||||||
Some(MomentValue::RangeFolded) => Rgba([119, 0, 125, 255]),
|
Some(MomentValue::RangeFolded) => Rgba([119, 0, 125, 255]),
|
||||||
Some(MomentValue::Value(value)) => {
|
Some(MomentValue::Value(value)) => {
|
||||||
let color = wxbox_pal::parser::parse(&data_source.palette)?.colorize(value as f64);
|
let color = wxbox_pal::parser::parse(&data_source.palette)?.colorize(value as f64);
|
||||||
Rgba([
|
Rgba([color.red, color.green, color.blue, color.alpha])
|
||||||
color.red,
|
|
||||||
color.green,
|
|
||||||
color.blue,
|
|
||||||
color.alpha,
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
None => Rgba([0, 0, 0, 30]),
|
None => Rgba([0, 0, 0, 30]),
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,16 +3,11 @@ use serde::Deserialize;
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
#[allow(non_snake_case)] // xml
|
#[allow(non_snake_case)] // xml
|
||||||
pub struct ListBucketResult {
|
pub struct ListBucketResult {
|
||||||
pub Name: String,
|
|
||||||
pub Prefix: String,
|
|
||||||
pub KeyCount: usize,
|
|
||||||
pub MaxKeys: usize,
|
|
||||||
|
|
||||||
pub Contents: Option<Vec<ListBucketResultContents>>,
|
pub Contents: Option<Vec<ListBucketResultContents>>,
|
||||||
}
|
}
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
#[allow(non_snake_case)] // xml
|
#[allow(non_snake_case)] // xml
|
||||||
pub struct ListBucketResultContents {
|
pub struct ListBucketResultContents {
|
||||||
pub Key: String,
|
pub Key: String,
|
||||||
pub LastModified: String
|
pub LastModified: String,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue