Lots of misc code changes

This commit is contained in:
Skye 2023-12-14 23:01:39 -05:00
parent c0f832bcef
commit 5fab9bc98b
13 changed files with 112 additions and 111 deletions

View file

@ -4,18 +4,17 @@ use crate::tiles;
use dbus::nonblock::SyncConnection; use dbus::nonblock::SyncConnection;
use log::error; use log::error;
use log::warn;
use serde::{Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
use smart_default::SmartDefault; use smart_default::SmartDefault;
use std::env::var;
use std::env::var;
use std::iter::FromIterator as _;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use structopt::StructOpt; use structopt::StructOpt;
use tokio::fs::File;
use tokio::io::AsyncReadExt;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
@ -97,16 +96,24 @@ pub struct HostnameConfig {
pub update: Duration, pub update: Duration,
} }
type StaticStr = &'static str;
#[derive(SmartDefault, Deserialize, Clone, Debug)] #[derive(SmartDefault, Deserialize, Clone, Debug)]
#[serde(default)] #[serde(default)]
pub struct IwdConfig { pub struct IwdConfig {
#[default("wlan0")] #[default("wlan0")]
pub interface: Box<str>, #[serde(deserialize_with = "leak_str")]
// Gotta obfuscate the static lifetime or derive(Deserialize) misbehaves
pub interface: StaticStr,
#[serde(deserialize_with = "deserialize_duration")] #[serde(deserialize_with = "deserialize_duration")]
#[default(_code = "Duration::from_secs(5)")] #[default(_code = "Duration::from_secs(5)")]
pub update: Duration, pub update: Duration,
} }
fn leak_str<'de, D: Deserializer<'de>>(deserializer: D) -> Result<&'static str, D::Error> {
Ok(Box::leak(Box::<str>::deserialize(deserializer)?))
}
#[derive(SmartDefault, Deserialize, Clone, Debug)] #[derive(SmartDefault, Deserialize, Clone, Debug)]
#[serde(default)] #[serde(default)]
pub struct UtcTimeConfig { pub struct UtcTimeConfig {
@ -173,31 +180,29 @@ struct Args {
pub async fn read_config() -> eyre::Result<Config> { pub async fn read_config() -> eyre::Result<Config> {
let args = Args::from_args(); let args = Args::from_args();
let config_path = match args.config { let Some(config_path) = args.config.clone().or_else(|| {
Some(config) => config, Some(if let Ok(rusty_conf) = var("RUSTYBAR_CONFIG") {
None => { rusty_conf.into()
if let Ok(rustybar_config_env) = var("RUSTYBAR_CONFIG") { } else if let Ok(config) = var("XDG_CONFIG_HOME") {
rustybar_config_env.into() PathBuf::from_iter([&config, "rustybar/config.toml"])
} else if let Ok(xdg_config_home) = var("XDG_CONFIG_HOME") { } else if let Ok(home) = var("HOME") {
[&xdg_config_home, "rustybar", "config.toml"] PathBuf::from_iter([&home, ".config/rustybar/config.toml"])
.iter() } else {
.collect() return None;
} else if let Ok(home) = var("HOME") { })
[&home, ".config", "rustybar", "config.toml"] }) else {
.iter() eyre::bail!(
.collect() "Could not find RUSTYBAR_CONFIG, XDG_CONFIG_HOME, or HOME environment variables"
} else { )
eyre::bail!("Could not find RUSTYBAR_CONFIG, XDG_CONFIG_HOME, or HOME environment variables");
}
}
}; };
let mut config_contents = vec![]; let string = tokio::fs::read_to_string(config_path)
File::open(config_path) .await
.await? .or_else(|e| match e.kind() {
.read_to_end(&mut config_contents) std::io::ErrorKind::NotFound => Ok(include_str!("../config.toml.example").to_owned()),
.await?; _ => Err(e),
Ok(toml::from_str(std::str::from_utf8(&config_contents)?)?) })?;
Ok(toml::from_str(&string)?)
} }
pub fn launch_tile( pub fn launch_tile(
@ -223,7 +228,7 @@ pub fn launch_tile(
} }
}; };
match result { match result {
Ok(_) => warn!("Tile {} exited without error", tile_id), Ok(t) => match t {},
Err(e) => error!("Tile {} exited with error: {:?}", tile_id, e), Err(e) => error!("Tile {} exited with error: {:?}", tile_id, e),
} }
}) })

View file

@ -45,12 +45,11 @@ pub async fn run(num_tiles: usize, mut receiver: mpsc::Receiver<TileData>) -> ey
.await .await
.ok_or_eyre("No more messages to recieve")?; .ok_or_eyre("No more messages to recieve")?;
if message.sender_id < num_tiles { let Some(block) = blocks.get_mut(message.sender_id) else {
blocks[message.sender_id] = Some(message.block);
} else {
eprintln!("Invalid message with sender id {}", message.sender_id); eprintln!("Invalid message with sender id {}", message.sender_id);
continue; continue;
} };
*block = Some(message.block);
let mut serialized = serde_json::to_vec(&blocks).unwrap(); let mut serialized = serde_json::to_vec(&blocks).unwrap();
serialized.extend_from_slice(b",\n"); serialized.extend_from_slice(b",\n");
stdout.write_all(&serialized).await?; stdout.write_all(&serialized).await?;

View file

@ -74,7 +74,7 @@ pub struct Block {
pub min_width: Option<u32>, pub min_width: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub align: Option<Alignment>, pub align: Option<Alignment>,
pub name: Box<str>, pub name: &'static str,
#[serde(serialize_with = "arc_default")] #[serde(serialize_with = "arc_default")]
#[default = ""] #[default = ""]
pub instance: Arc<str>, pub instance: Arc<str>,

View file

@ -2,12 +2,12 @@ use crate::config::BatteryConfig;
use crate::output::OutputChannel; use crate::output::OutputChannel;
use crate::tile::Block; use crate::tile::Block;
use std::convert::Infallible;
use std::path::Path; use std::path::Path;
use tokio::fs::{try_exists, File}; use tokio::fs::try_exists;
use tokio::io::AsyncReadExt;
use tokio::try_join; use tokio::try_join;
pub async fn battery(config: BatteryConfig, output: OutputChannel) -> eyre::Result<()> { pub async fn battery(config: BatteryConfig, output: OutputChannel) -> eyre::Result<Infallible> {
let base_dir = Path::new("/sys/class/power_supply").join(&*config.battery); let base_dir = Path::new("/sys/class/power_supply").join(&*config.battery);
let file_prefix = if try_exists(base_dir.join("energy_now")).await? { let file_prefix = if try_exists(base_dir.join("energy_now")).await? {
"energy_" "energy_"
@ -16,45 +16,26 @@ pub async fn battery(config: BatteryConfig, output: OutputChannel) -> eyre::Resu
}; };
let now_path = base_dir.join(file_prefix.to_owned() + "now"); let now_path = base_dir.join(file_prefix.to_owned() + "now");
let full_path = base_dir.join(file_prefix.to_owned() + "full"); let full_path = base_dir.join(file_prefix.to_owned() + "full");
let status_path = base_dir.join("status");
let mut interval = tokio::time::interval(config.update); let mut interval = tokio::time::interval(config.update);
loop { loop {
interval.tick().await; interval.tick().await;
let charge_now = async { async fn read_file_to_string(path: &Path) -> eyre::Result<u32> {
let mut raw = String::new(); Ok(tokio::fs::read_to_string(path).await?.trim_end().parse()?)
File::open(&now_path) }
.await?
.read_to_string(&mut raw) let charge_now = read_file_to_string(&now_path);
.await?; let charge_total = read_file_to_string(&full_path);
let charge: u32 = raw.trim_end().parse()?; let status = read_file_to_string(&status_path);
eyre::Result::<_>::Ok(charge)
};
let charge_total = async {
let mut raw = String::new();
File::open(&full_path)
.await?
.read_to_string(&mut raw)
.await?;
let charge: u32 = raw.trim_end().parse()?;
eyre::Result::<_>::Ok(charge)
};
let status = async {
let mut raw = String::new();
File::open(base_dir.join("status"))
.await?
.read_to_string(&mut raw)
.await?;
raw.truncate(raw.trim_end().len());
eyre::Result::<_>::Ok(raw)
};
let (charge_now, charge_total, status) = try_join!(charge_now, charge_total, status)?; let (charge_now, charge_total, status) = try_join!(charge_now, charge_total, status)?;
let percentage = charge_now * 100 / charge_total; let percentage = charge_now * 100 / charge_total;
output output
.send(Block { .send(Block {
full_text: format!("{}% {}", percentage, status).into(), full_text: format!("{}% {}", percentage, status).into(),
short_text: format!("{}%", percentage).into_boxed_str().into(), short_text: format!("{}%", percentage).into_boxed_str().into(),
name: "battery".into(), name: "battery",
..Default::default() ..Default::default()
}) })
.await?; .await?;

View file

@ -3,6 +3,7 @@ use crate::generated::OrgFreedesktopHostname1;
use crate::output::OutputChannel; use crate::output::OutputChannel;
use crate::tile::Block; use crate::tile::Block;
use dbus::nonblock::{Proxy, SyncConnection}; use dbus::nonblock::{Proxy, SyncConnection};
use std::convert::Infallible;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
@ -10,7 +11,7 @@ pub async fn hostname(
config: HostnameConfig, config: HostnameConfig,
output: OutputChannel, output: OutputChannel,
dbus_conn: Arc<SyncConnection>, dbus_conn: Arc<SyncConnection>,
) -> eyre::Result<()> { ) -> eyre::Result<Infallible> {
let proxy = Proxy::new( let proxy = Proxy::new(
"org.freedesktop.hostname1", "org.freedesktop.hostname1",
"/org/freedesktop/hostname1", "/org/freedesktop/hostname1",
@ -26,7 +27,7 @@ pub async fn hostname(
output output
.send(Block { .send(Block {
full_text: hostname.into(), full_text: hostname.into(),
name: "hostname".into(), name: "hostname",
..Default::default() ..Default::default()
}) })
.await?; .await?;

View file

@ -1,10 +1,15 @@
use std::convert::Infallible;
use crate::config::InternetTimeConfig; use crate::config::InternetTimeConfig;
use crate::output::OutputChannel; use crate::output::OutputChannel;
use crate::tile::Block; use crate::tile::Block;
use chrono::prelude::*; use chrono::prelude::*;
use tokio::time::sleep; use tokio::time::sleep;
pub async fn internet_time(config: InternetTimeConfig, output: OutputChannel) -> eyre::Result<()> { pub async fn internet_time(
config: InternetTimeConfig,
output: OutputChannel,
) -> eyre::Result<Infallible> {
// "BMT" is actually just UTC+1 // "BMT" is actually just UTC+1
let offset = FixedOffset::east_opt(60 * 60).unwrap(); let offset = FixedOffset::east_opt(60 * 60).unwrap();
let factor = 10f64.powi(config.precision.into()); let factor = 10f64.powi(config.precision.into());
@ -18,15 +23,14 @@ pub async fn internet_time(config: InternetTimeConfig, output: OutputChannel) ->
output output
.send(Block { .send(Block {
full_text: format!( full_text: format!(
"@{time:0width$.prec$}", "@{internet_time:0width$.prec$}",
time = internet_time,
// 3 digits + and decimal point // 3 digits + and decimal point
width = config.precision as usize + 4, width = config.precision as usize + 4,
prec = config.precision as usize prec = config.precision as usize
) )
.into_boxed_str(), .into_boxed_str(),
short_text: Some(format!("@{:03.0}", internet_time).into_boxed_str()), short_text: Some(format!("@{:03.0}", internet_time).into_boxed_str()),
name: "internet_time".into(), name: "internet_time",
..Default::default() ..Default::default()
}) })
.await?; .await?;

View file

@ -7,6 +7,7 @@ use dbus::nonblock::{Proxy, SyncConnection};
use dbus::Path; use dbus::Path;
use eyre::OptionExt; use eyre::OptionExt;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::Infallible;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
@ -32,7 +33,7 @@ pub async fn iwd(
config: IwdConfig, config: IwdConfig,
output: OutputChannel, output: OutputChannel,
dbus_conn: Arc<SyncConnection>, dbus_conn: Arc<SyncConnection>,
) -> eyre::Result<()> { ) -> eyre::Result<Infallible> {
let root_proxy = Proxy::new("net.connman.iwd", "/", Duration::from_secs(5), dbus_conn); let root_proxy = Proxy::new("net.connman.iwd", "/", Duration::from_secs(5), dbus_conn);
let mut interval = tokio::time::interval(config.update); let mut interval = tokio::time::interval(config.update);
@ -40,7 +41,7 @@ pub async fn iwd(
interval.tick().await; interval.tick().await;
let managed_objects = root_proxy.get_managed_objects().await?; let managed_objects = root_proxy.get_managed_objects().await?;
let interface_path = get_interface_path(&managed_objects, &config.interface) let interface_path = get_interface_path(&managed_objects, config.interface)
.ok_or_eyre("Couldn't find interface")?; .ok_or_eyre("Couldn't find interface")?;
let station_path = managed_objects let station_path = managed_objects
.get(&interface_path) .get(&interface_path)
@ -62,7 +63,7 @@ pub async fn iwd(
output output
.send(Block { .send(Block {
full_text: text.into(), full_text: text.into(),
name: config.interface.clone(), name: config.interface,
..Default::default() ..Default::default()
}) })
.await?; .await?;

View file

@ -1,22 +1,22 @@
use std::convert::Infallible;
use crate::config::LoadConfig; use crate::config::LoadConfig;
use crate::output::OutputChannel; use crate::output::OutputChannel;
use crate::tile::Block; use crate::tile::Block;
use tokio::fs::File;
use tokio::io::AsyncReadExt;
pub async fn load(config: LoadConfig, output: OutputChannel) -> eyre::Result<()> { pub async fn load(config: LoadConfig, output: OutputChannel) -> eyre::Result<Infallible> {
let mut interval = tokio::time::interval(config.update); let mut interval = tokio::time::interval(config.update);
loop { loop {
interval.tick().await; interval.tick().await;
let mut raw = String::new(); let raw = tokio::fs::read_to_string("/proc/loadavg").await?;
let mut file = File::open("/proc/loadavg").await?; let Some((load, _)) = raw.split_once(' ') else {
file.read_to_string(&mut raw).await?; continue;
let (load, _rest) = raw.split_at(raw.find(' ').unwrap_or(0)); };
output output
.send(Block { .send(Block {
full_text: load.into(), full_text: load.into(),
name: "load".into(), name: "load",
..Default::default() ..Default::default()
}) })
.await?; .await?;

View file

@ -4,6 +4,7 @@ use crate::tile::Block;
use chrono::prelude::*; use chrono::prelude::*;
use maxminddb::geoip2; use maxminddb::geoip2;
use std::convert::Infallible;
use std::net::IpAddr; use std::net::IpAddr;
use std::str::FromStr; use std::str::FromStr;
use tokio::sync::watch; use tokio::sync::watch;
@ -66,12 +67,14 @@ async fn find_time_zone(config: LocalTimeConfig, sender: watch::Sender<State>) -
} }
} }
pub async fn local_time(config: LocalTimeConfig, output: OutputChannel) -> eyre::Result<()> { pub async fn local_time(
let fallback_tz = if let Some(tz_name) = &config.fallback_zone { config: LocalTimeConfig,
chrono_tz::Tz::from_str(tz_name).ok() output: OutputChannel,
} else { ) -> eyre::Result<Infallible> {
None let fallback_tz = config
}; .fallback_zone
.as_ref()
.and_then(|tz_name| chrono_tz::Tz::from_str(tz_name).ok());
let (sender, mut receiver) = watch::channel(State { tz: fallback_tz }); let (sender, mut receiver) = watch::channel(State { tz: fallback_tz });
let config_clone = config.clone(); let config_clone = config.clone();
@ -97,7 +100,7 @@ pub async fn local_time(config: LocalTimeConfig, output: OutputChannel) -> eyre:
.send(Block { .send(Block {
full_text: local_now.format(&config.format).to_string().into(), full_text: local_now.format(&config.format).to_string().into(),
short_text: Some(local_now.format(&config.short_format).to_string().into()), short_text: Some(local_now.format(&config.short_format).to_string().into()),
name: "local_time".into(), name: "local_time",
..Default::default() ..Default::default()
}) })
.await?; .await?;

View file

@ -1,6 +1,7 @@
use crate::config::MemoryConfig; use crate::config::MemoryConfig;
use crate::output::OutputChannel; use crate::output::OutputChannel;
use crate::tile::Block; use crate::tile::Block;
use std::convert::Infallible;
use std::{io, str, u64}; use std::{io, str, u64};
use tokio::fs::File; use tokio::fs::File;
use tokio::io::AsyncReadExt; use tokio::io::AsyncReadExt;
@ -9,11 +10,11 @@ fn prettify_kib(kib: u64) -> Box<str> {
let (mem, unit) = match kib { let (mem, unit) = match kib {
0..=0x3ff => (kib, 'k'), 0..=0x3ff => (kib, 'k'),
0x400..=0xfffff => (kib >> 10, 'M'), 0x400..=0xfffff => (kib >> 10, 'M'),
0x100000..=0x3fffffff => (kib >> 20, 'G'), 0x10_0000..=0x3fff_ffff => (kib >> 20, 'G'),
0x40000000..=0xffffffffff => (kib >> 30, 'T'), 0x4000_0000..=0xff_ffff_ffff => (kib >> 30, 'T'),
0x10000000000..=0x3ffffffffffff => (kib >> 40, 'P'), 0x100_0000_0000..=0x3_ffff_ffff_ffff => (kib >> 40, 'P'),
0x4000000000000..=0xfffffffffffffff => (kib >> 50, 'E'), 0x4_0000_0000_0000..=0xfff_ffff_ffff_ffff => (kib >> 50, 'E'),
0x1000000000000000..=0xffffffffffffffff => (kib >> 60, 'Z'), 0x1000_0000_0000_0000..=0xffff_ffff_ffff_ffff => (kib >> 60, 'Z'),
}; };
format!("{} {}iB", mem, unit).into_boxed_str() format!("{} {}iB", mem, unit).into_boxed_str()
} }
@ -27,7 +28,7 @@ fn extract_value(line: &str) -> eyre::Result<u64> {
.parse()?) .parse()?)
} }
pub async fn memory(config: MemoryConfig, output: OutputChannel) -> eyre::Result<()> { pub async fn memory(config: MemoryConfig, output: OutputChannel) -> eyre::Result<Infallible> {
let mut interval = tokio::time::interval(config.update); let mut interval = tokio::time::interval(config.update);
loop { loop {
interval.tick().await; interval.tick().await;
@ -36,7 +37,7 @@ pub async fn memory(config: MemoryConfig, output: OutputChannel) -> eyre::Result
let mut file = File::open("/proc/meminfo").await?; let mut file = File::open("/proc/meminfo").await?;
file.read_exact(&mut raw).await?; file.read_exact(&mut raw).await?;
let string_data = str::from_utf8(&raw)?; let string_data = str::from_utf8(&raw)?;
let mut lines = string_data.split('\n'); let mut lines = string_data.lines();
let mem_total = prettify_kib(extract_value( let mem_total = prettify_kib(extract_value(
lines lines
.next() .next()
@ -56,7 +57,7 @@ pub async fn memory(config: MemoryConfig, output: OutputChannel) -> eyre::Result
.send(Block { .send(Block {
full_text, full_text,
short_text: Some(short_text), short_text: Some(short_text),
name: "memory".into(), name: "memory",
..Default::default() ..Default::default()
}) })
.await?; .await?;

View file

@ -3,21 +3,22 @@ use crate::generated::OrgFreedesktopNetworkManager;
use crate::generated::OrgFreedesktopNetworkManagerAccessPoint; use crate::generated::OrgFreedesktopNetworkManagerAccessPoint;
use crate::generated::OrgFreedesktopNetworkManagerConnectionActive; use crate::generated::OrgFreedesktopNetworkManagerConnectionActive;
use crate::output::OutputChannel; use crate::output::OutputChannel;
use crate::tile::Block;
use dbus::nonblock::{Proxy, SyncConnection}; use dbus::nonblock::{Proxy, SyncConnection};
use std::convert::Infallible;
use std::future::pending;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
pub async fn network_manager( pub async fn network_manager(
config: NetworkManagerConfig, _config: NetworkManagerConfig,
output: OutputChannel, _output: OutputChannel,
dbus_conn: Arc<SyncConnection>, dbus_conn: Arc<SyncConnection>,
) -> eyre::Result<()> { ) -> eyre::Result<Infallible> {
let root_proxy = Proxy::new( let root_proxy = Proxy::new(
"org.freedesktop.NetworkManager", "org.freedesktop.NetworkManager",
"/org/freedesktop/NetworkManager", "/org/freedesktop/NetworkManager",
Duration::from_secs(5), Duration::from_secs(5),
dbus_conn.clone(), dbus_conn.as_ref(),
); );
let primary_connection = root_proxy.primary_connection().await?; let primary_connection = root_proxy.primary_connection().await?;
@ -25,7 +26,7 @@ pub async fn network_manager(
"org.freedesktop.NetworkManager", "org.freedesktop.NetworkManager",
primary_connection, primary_connection,
Duration::from_secs(5), Duration::from_secs(5),
dbus_conn.clone(), dbus_conn.as_ref(),
); );
let specific_object = primary_connection_proxy.specific_object().await?; let specific_object = primary_connection_proxy.specific_object().await?;
@ -33,14 +34,12 @@ pub async fn network_manager(
"org.freedesktop.NetworkManager", "org.freedesktop.NetworkManager",
specific_object, specific_object,
Duration::from_secs(5), Duration::from_secs(5),
dbus_conn.clone(), dbus_conn.as_ref(),
); );
let ssid = specific_object_proxy.ssid().await?; let ssid = specific_object_proxy.ssid().await?;
log::warn!("got ssid: {}", String::from_utf8_lossy(&ssid)); log::warn!("got ssid: {}", String::from_utf8_lossy(&ssid));
loop { pending().await
tokio::time::sleep(Duration::from_secs(3600)).await;
}
} }

View file

@ -1,17 +1,22 @@
use std::convert::Infallible;
use crate::config::SystemTimeConfig; use crate::config::SystemTimeConfig;
use crate::output::OutputChannel; use crate::output::OutputChannel;
use crate::tile::Block; use crate::tile::Block;
use chrono::prelude::*; use chrono::prelude::*;
use tokio::time::sleep; use tokio::time::sleep;
pub async fn system_time(config: SystemTimeConfig, output: OutputChannel) -> eyre::Result<()> { pub async fn system_time(
config: SystemTimeConfig,
output: OutputChannel,
) -> eyre::Result<Infallible> {
loop { loop {
let now = Local::now(); let now = Local::now();
output output
.send(Block { .send(Block {
full_text: now.format(&config.format).to_string().into(), full_text: now.format(&config.format).to_string().into(),
short_text: Some(now.format(&config.short_format).to_string().into()), short_text: Some(now.format(&config.short_format).to_string().into()),
name: "system_time".into(), name: "system_time",
..Default::default() ..Default::default()
}) })
.await?; .await?;

View file

@ -1,17 +1,19 @@
use std::convert::Infallible;
use crate::config::UtcTimeConfig; use crate::config::UtcTimeConfig;
use crate::output::OutputChannel; use crate::output::OutputChannel;
use crate::tile::Block; use crate::tile::Block;
use chrono::prelude::*; use chrono::prelude::*;
use tokio::time::sleep; use tokio::time::sleep;
pub async fn utc_time(config: UtcTimeConfig, output: OutputChannel) -> eyre::Result<()> { pub async fn utc_time(config: UtcTimeConfig, output: OutputChannel) -> eyre::Result<Infallible> {
loop { loop {
let now = Utc::now(); let now = Utc::now();
output output
.send(Block { .send(Block {
full_text: now.format(&config.format).to_string().into(), full_text: now.format(&config.format).to_string().into(),
short_text: Some(now.format(&config.short_format).to_string().into()), short_text: Some(now.format(&config.short_format).to_string().into()),
name: "utc_time".into(), name: "utc_time",
..Default::default() ..Default::default()
}) })
.await?; .await?;