mirror of
https://github.com/sergi0g/cup.git
synced 2025-11-18 01:43:41 -05:00
Various small changes and optimizations related to timekeeping, logging and JSON handling. Binary size shrunk by 0.2 MB!
This commit is contained in:
@@ -39,5 +39,5 @@ pub fn print_updates(updates: &[Image], icons: &bool) {
|
||||
}
|
||||
|
||||
pub fn print_raw_updates(updates: &[Image]) {
|
||||
println!("{}", json::stringify(to_simple_json(updates)));
|
||||
println!("{}", to_simple_json(updates));
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use formatting::{print_raw_updates, print_updates};
|
||||
#[cfg(feature = "server")]
|
||||
use server::serve;
|
||||
use std::path::PathBuf;
|
||||
use utils::misc::timestamp;
|
||||
use std::time::SystemTime;
|
||||
|
||||
pub mod check;
|
||||
pub mod config;
|
||||
@@ -81,7 +81,7 @@ async fn main() {
|
||||
icons,
|
||||
raw,
|
||||
}) => {
|
||||
let start = timestamp();
|
||||
let start = SystemTime::now();
|
||||
match *raw || config.debug {
|
||||
true => {
|
||||
let updates = get_updates(references, &config).await;
|
||||
@@ -91,9 +91,8 @@ async fn main() {
|
||||
let spinner = Spinner::new();
|
||||
let updates = get_updates(references, &config).await;
|
||||
spinner.succeed();
|
||||
let end = timestamp();
|
||||
print_updates(&updates, icons);
|
||||
info!("✨ Checked {} images in {}ms", updates.len(), end - start);
|
||||
info!("✨ Checked {} images in {}ms", updates.len(), start.elapsed().unwrap().as_millis());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::time::SystemTime;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
@@ -10,10 +12,9 @@ use crate::{
|
||||
},
|
||||
utils::{
|
||||
link::parse_link,
|
||||
misc::timestamp,
|
||||
request::{
|
||||
get_protocol, get_response_body, parse_json, parse_www_authenticate, to_bearer_string,
|
||||
},
|
||||
}, time::{elapsed, now},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -50,7 +51,7 @@ pub async fn get_latest_digest(
|
||||
config.debug,
|
||||
"Checking for digest update to {}", image.reference
|
||||
);
|
||||
let start = timestamp();
|
||||
let start = SystemTime::now();
|
||||
let protocol = get_protocol(&image.registry, &config.registries);
|
||||
let url = format!(
|
||||
"{}://{}/v2/{}/manifests/{}",
|
||||
@@ -60,7 +61,7 @@ pub async fn get_latest_digest(
|
||||
let headers = vec![("Accept", Some("application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.index.v1+json")), ("Authorization", authorization.as_deref())];
|
||||
|
||||
let response = client.head(&url, headers).await;
|
||||
let time = timestamp() - start;
|
||||
let time = start.elapsed().unwrap().as_millis() as u32;
|
||||
debug!(
|
||||
config.debug,
|
||||
"Checked for digest update to {} in {}ms", image.reference, time
|
||||
@@ -112,7 +113,7 @@ pub async fn get_token(
|
||||
Ok(response) => parse_json(&get_response_body(response).await),
|
||||
Err(_) => error!("GET {}: Request failed!", url),
|
||||
};
|
||||
response_json["token"].to_string()
|
||||
response_json["token"].as_str().unwrap().to_string()
|
||||
}
|
||||
|
||||
pub async fn get_latest_tag(
|
||||
@@ -126,7 +127,7 @@ pub async fn get_latest_tag(
|
||||
config.debug,
|
||||
"Checking for tag update to {}", image.reference
|
||||
);
|
||||
let start = timestamp();
|
||||
let start = now();
|
||||
let protocol = get_protocol(&image.registry, &config.registries);
|
||||
let url = format!(
|
||||
"{}://{}/v2/{}/tags/list",
|
||||
@@ -148,25 +149,31 @@ pub async fn get_latest_tag(
|
||||
image.reference,
|
||||
tags.len()
|
||||
);
|
||||
let (new_tags, next) =
|
||||
match get_extra_tags(&next_url.unwrap(), headers.clone(), base, &image.version_info.as_ref().unwrap().format_str, client).await {
|
||||
Ok(t) => t,
|
||||
Err(message) => {
|
||||
return Image {
|
||||
error: Some(message),
|
||||
time_ms: image.time_ms + (timestamp() - start),
|
||||
..image.clone()
|
||||
}
|
||||
let (new_tags, next) = match get_extra_tags(
|
||||
&next_url.unwrap(),
|
||||
headers.clone(),
|
||||
base,
|
||||
&image.version_info.as_ref().unwrap().format_str,
|
||||
client,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(t) => t,
|
||||
Err(message) => {
|
||||
return Image {
|
||||
error: Some(message),
|
||||
time_ms: image.time_ms + elapsed(start),
|
||||
..image.clone()
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
tags.extend_from_slice(&new_tags);
|
||||
next_url = next;
|
||||
}
|
||||
let tag = tags.iter().max();
|
||||
let time = timestamp() - start;
|
||||
debug!(
|
||||
config.debug,
|
||||
"Checked for tag update to {} in {}ms", image.reference, time
|
||||
"Checked for tag update to {} in {}ms", image.reference, elapsed(start)
|
||||
);
|
||||
match tag {
|
||||
Some(t) => {
|
||||
@@ -178,7 +185,7 @@ pub async fn get_latest_tag(
|
||||
latest_remote_tag: Some(t.clone()),
|
||||
..image.version_info.as_ref().unwrap().clone()
|
||||
}),
|
||||
time_ms: image.time_ms + time,
|
||||
time_ms: image.time_ms + elapsed(start),
|
||||
..image.clone()
|
||||
},
|
||||
token,
|
||||
@@ -192,7 +199,7 @@ pub async fn get_latest_tag(
|
||||
latest_remote_tag: Some(t.clone()),
|
||||
..image.version_info.as_ref().unwrap().clone()
|
||||
}),
|
||||
time_ms: image.time_ms + time,
|
||||
time_ms: image.time_ms + elapsed(start),
|
||||
..image.clone()
|
||||
}
|
||||
}
|
||||
@@ -218,11 +225,14 @@ pub async fn get_extra_tags(
|
||||
.map(|link| parse_link(link.to_str().unwrap(), url));
|
||||
let response_json = parse_json(&get_response_body(res).await);
|
||||
let result = response_json["tags"]
|
||||
.members()
|
||||
.filter_map(|tag| Version::from_tag(&tag.to_string()))
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter_map(|tag| Version::from_tag(tag.as_str().unwrap()))
|
||||
.filter(|(tag, format_string)| match (base.minor, tag.minor) {
|
||||
(Some(_), Some(_)) | (None, None) => {
|
||||
matches!((base.patch, tag.patch), (Some(_), Some(_)) | (None, None)) && format_str == *format_string
|
||||
matches!((base.patch, tag.patch), (Some(_), Some(_)) | (None, None))
|
||||
&& format_str == *format_string
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
|
||||
104
src/server.rs
104
src/server.rs
@@ -1,16 +1,17 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono::Local;
|
||||
use json::JsonValue;
|
||||
use liquid::{object, Object, ValueView};
|
||||
use serde_json::Value;
|
||||
use tokio::sync::Mutex;
|
||||
use xitca_web::{
|
||||
body::ResponseBody,
|
||||
error::Error,
|
||||
handler::{handler_service, path::PathRef, state::StateRef},
|
||||
http::WebResponse,
|
||||
middleware::Logger,
|
||||
http::{StatusCode, WebResponse},
|
||||
route::get,
|
||||
App,
|
||||
service::Service,
|
||||
App, WebContext,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -20,8 +21,8 @@ use crate::{
|
||||
structs::image::Image,
|
||||
utils::{
|
||||
json::{to_full_json, to_simple_json},
|
||||
misc::timestamp,
|
||||
sort_update_vec::sort_image_vec,
|
||||
time::{elapsed, now},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -59,7 +60,7 @@ pub async fn serve(port: &u16, config: &Config) -> std::io::Result<()> {
|
||||
.at("/*", get(handler_service(_static)));
|
||||
}
|
||||
app_builder
|
||||
.enclosed(Logger::new())
|
||||
.enclosed_fn(logger)
|
||||
.serve()
|
||||
.bind(format!("0.0.0.0:{}", port))?
|
||||
.run()
|
||||
@@ -105,18 +106,18 @@ async fn _static(data: StateRef<'_, Arc<Mutex<ServerData>>>, path: PathRef<'_>)
|
||||
async fn api_simple(data: StateRef<'_, Arc<Mutex<ServerData>>>) -> WebResponse {
|
||||
WebResponse::builder()
|
||||
.header("Content-Type", "application/json")
|
||||
.body(ResponseBody::from(json::stringify(
|
||||
data.lock().await.simple_json.clone(),
|
||||
)))
|
||||
.body(ResponseBody::from(
|
||||
data.lock().await.simple_json.clone().to_string(),
|
||||
))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn api_full(data: StateRef<'_, Arc<Mutex<ServerData>>>) -> WebResponse {
|
||||
WebResponse::builder()
|
||||
.header("Content-Type", "application/json")
|
||||
.body(ResponseBody::from(json::stringify(
|
||||
data.lock().await.full_json.clone(),
|
||||
)))
|
||||
.body(ResponseBody::from(
|
||||
data.lock().await.full_json.clone().to_string(),
|
||||
))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -128,8 +129,8 @@ async fn refresh(data: StateRef<'_, Arc<Mutex<ServerData>>>) -> WebResponse {
|
||||
struct ServerData {
|
||||
template: String,
|
||||
raw_updates: Vec<Image>,
|
||||
simple_json: JsonValue,
|
||||
full_json: JsonValue,
|
||||
simple_json: Value,
|
||||
full_json: Value,
|
||||
config: Config,
|
||||
theme: &'static str,
|
||||
}
|
||||
@@ -139,8 +140,8 @@ impl ServerData {
|
||||
let mut s = Self {
|
||||
config: config.clone(),
|
||||
template: String::new(),
|
||||
simple_json: JsonValue::Null,
|
||||
full_json: JsonValue::Null,
|
||||
simple_json: Value::Null,
|
||||
full_json: Value::Null,
|
||||
raw_updates: Vec::new(),
|
||||
theme: "neutral",
|
||||
};
|
||||
@@ -148,13 +149,16 @@ impl ServerData {
|
||||
s
|
||||
}
|
||||
async fn refresh(&mut self) {
|
||||
let start = timestamp();
|
||||
let start = now();
|
||||
if !self.raw_updates.is_empty() {
|
||||
info!("Refreshing data");
|
||||
}
|
||||
let updates = sort_image_vec(&get_updates(&None, &self.config).await);
|
||||
let end = timestamp();
|
||||
info!("✨ Checked {} images in {}ms", updates.len(), end - start);
|
||||
info!(
|
||||
"✨ Checked {} images in {}ms",
|
||||
updates.len(),
|
||||
elapsed(start)
|
||||
);
|
||||
self.raw_updates = updates;
|
||||
let template = liquid::ParserBuilder::with_stdlib()
|
||||
.build()
|
||||
@@ -173,16 +177,29 @@ impl ServerData {
|
||||
.to_rfc3339_opts(chrono::SecondsFormat::Secs, true)
|
||||
.to_string()
|
||||
.into();
|
||||
self.full_json["last_updated"] = last_updated
|
||||
.to_rfc3339_opts(chrono::SecondsFormat::Secs, true)
|
||||
.to_string()
|
||||
.into();
|
||||
self.full_json["last_updated"] = self.simple_json["last_updated"].clone();
|
||||
self.theme = match &self.config.theme {
|
||||
Theme::Default => "neutral",
|
||||
Theme::Blue => "gray",
|
||||
};
|
||||
let mut metrics = self.simple_json["metrics"].entries().map(|(key, value)| liquid::object!({ "name": key, "value": value.as_u16().unwrap()})).collect::<Vec<_>>();
|
||||
metrics.sort_unstable_by(|a, b| {dbg!(a, b); SORT_ORDER.iter().position(|i| i == &a["name"].to_kstr().as_str()).unwrap().cmp(&SORT_ORDER.iter().position(|i| i == &b["name"].to_kstr().as_str()).unwrap())});
|
||||
let mut metrics = self.simple_json["metrics"]
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|(key, value)| liquid::object!({ "name": key, "value": value }))
|
||||
.collect::<Vec<_>>();
|
||||
metrics.sort_unstable_by(|a, b| {
|
||||
SORT_ORDER
|
||||
.iter()
|
||||
.position(|i| i == &a["name"].to_kstr().as_str())
|
||||
.unwrap()
|
||||
.cmp(
|
||||
&SORT_ORDER
|
||||
.iter()
|
||||
.position(|i| i == &b["name"].to_kstr().as_str())
|
||||
.unwrap(),
|
||||
)
|
||||
});
|
||||
let globals = object!({
|
||||
"metrics": metrics,
|
||||
"images": images,
|
||||
@@ -192,3 +209,40 @@ impl ServerData {
|
||||
self.template = template.render(&globals).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
async fn logger<S, C, B>(next: &S, ctx: WebContext<'_, C, B>) -> Result<WebResponse, Error<C>>
|
||||
where
|
||||
S: for<'r> Service<WebContext<'r, C, B>, Response = WebResponse, Error = Error<C>>,
|
||||
{
|
||||
let start = now();
|
||||
let request = ctx.req();
|
||||
let method = request.method().to_string();
|
||||
let url = request.uri().to_string();
|
||||
|
||||
if &method != "GET" {
|
||||
// We only allow GET requests
|
||||
|
||||
log(&method, &url, 405, elapsed(start));
|
||||
Err(Error::from(StatusCode::METHOD_NOT_ALLOWED))
|
||||
} else {
|
||||
let res = next.call(ctx).await?;
|
||||
let status = res.status().as_u16();
|
||||
|
||||
log(&method, &url, status, elapsed(start));
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
fn log(method: &str, url: &str, status: u16, time: u32) {
|
||||
let color = {
|
||||
if status == 200 {
|
||||
"\x1b[32m"
|
||||
} else {
|
||||
"\x1b[31m"
|
||||
}
|
||||
};
|
||||
println!(
|
||||
"\x1b[94;1mHTTP \x1b[0m\x1b[32m{}\x1b[0m {} {}{}\x1b[0m in {}ms",
|
||||
method, url, color, status, time
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use json::{object, JsonValue};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::{
|
||||
config::Config,
|
||||
@@ -38,7 +38,7 @@ pub struct Image {
|
||||
pub digest_info: Option<DigestInfo>,
|
||||
pub version_info: Option<VersionInfo>,
|
||||
pub error: Option<String>,
|
||||
pub time_ms: i64,
|
||||
pub time_ms: u32,
|
||||
}
|
||||
|
||||
impl Image {
|
||||
@@ -127,23 +127,23 @@ impl Image {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts image data into a `JsonValue`
|
||||
pub fn to_json(&self) -> JsonValue {
|
||||
/// Converts image data into a `Value`
|
||||
pub fn to_json(&self) -> Value {
|
||||
let has_update = self.has_update();
|
||||
let update_type = match has_update {
|
||||
Status::UpdateMajor | Status::UpdateMinor | Status::UpdatePatch => "version",
|
||||
_ => "digest",
|
||||
};
|
||||
object! {
|
||||
reference: self.reference.clone(),
|
||||
parts: object! {
|
||||
registry: self.registry.clone(),
|
||||
repository: self.repository.clone(),
|
||||
tag: self.tag.clone()
|
||||
json!({
|
||||
"reference": self.reference.clone(),
|
||||
"parts": {
|
||||
"registry": self.registry.clone(),
|
||||
"repository": self.repository.clone(),
|
||||
"tag": self.tag.clone()
|
||||
},
|
||||
result: object! {
|
||||
has_update: has_update.to_option_bool(),
|
||||
info: match has_update {
|
||||
"result": {
|
||||
"has_update": has_update.to_option_bool(),
|
||||
"info": match has_update {
|
||||
Status::Unknown(_) => None,
|
||||
_ => Some(match update_type {
|
||||
"version" => {
|
||||
@@ -151,35 +151,35 @@ impl Image {
|
||||
Some(data) => (data.current_tag.clone(), data.latest_remote_tag.clone()),
|
||||
_ => unreachable!()
|
||||
};
|
||||
object! {
|
||||
json!({
|
||||
"type": update_type,
|
||||
version_update_type: match has_update {
|
||||
"version_update_type": match has_update {
|
||||
Status::UpdateMajor => "major",
|
||||
Status::UpdateMinor => "minor",
|
||||
Status::UpdatePatch => "patch",
|
||||
_ => unreachable!()
|
||||
},
|
||||
new_version: self.tag.replace(&version_tag.to_string(), &latest_remote_tag.as_ref().unwrap().to_string())
|
||||
}
|
||||
"new_version": self.tag.replace(&version_tag.to_string(), &latest_remote_tag.as_ref().unwrap().to_string())
|
||||
})
|
||||
},
|
||||
"digest" => {
|
||||
let (local_digests, remote_digest) = match &self.digest_info {
|
||||
Some(data) => (data.local_digests.clone(), data.remote_digest.clone()),
|
||||
_ => unreachable!()
|
||||
};
|
||||
object! {
|
||||
json!({
|
||||
"type": update_type,
|
||||
local_digests: local_digests,
|
||||
remote_digest: remote_digest,
|
||||
}
|
||||
"local_digests": local_digests,
|
||||
"remote_digest": remote_digest,
|
||||
})
|
||||
},
|
||||
_ => unreachable!()
|
||||
}),
|
||||
},
|
||||
error: self.error.clone()
|
||||
"error": self.error.clone()
|
||||
},
|
||||
time: self.time_ms
|
||||
}
|
||||
"time": self.time_ms
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks if the image has an update
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Functions that return JSON data, used for generating output and API responses
|
||||
|
||||
use json::{object, JsonValue};
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
use crate::structs::{image::Image, status::Status};
|
||||
|
||||
/// Helper function to get metrics used in JSON output
|
||||
pub fn get_metrics(updates: &[Image]) -> JsonValue {
|
||||
pub fn get_metrics(updates: &[Image]) -> Value {
|
||||
let mut up_to_date = 0;
|
||||
let mut major_updates = 0;
|
||||
let mut minor_updates = 0;
|
||||
@@ -35,34 +35,38 @@ pub fn get_metrics(updates: &[Image]) -> JsonValue {
|
||||
}
|
||||
};
|
||||
});
|
||||
object! {
|
||||
monitored_images: updates.len(),
|
||||
up_to_date: up_to_date,
|
||||
updates_available: major_updates + minor_updates + patch_updates + other_updates,
|
||||
major_updates: major_updates,
|
||||
minor_updates: minor_updates,
|
||||
patch_updates: patch_updates,
|
||||
other_updates: other_updates,
|
||||
unknown: unknown
|
||||
}
|
||||
json!({
|
||||
"monitored_images": updates.len(),
|
||||
"up_to_date": up_to_date,
|
||||
"updates_available": major_updates + minor_updates + patch_updates + other_updates,
|
||||
"major_updates": major_updates,
|
||||
"minor_updates": minor_updates,
|
||||
"patch_updates": patch_updates,
|
||||
"other_updates": other_updates,
|
||||
"unknown": unknown
|
||||
})
|
||||
}
|
||||
|
||||
/// Takes a slice of `Image` objects and returns a `JsonValue` of update info. The output doesn't contain much detail
|
||||
pub fn to_simple_json(updates: &[Image]) -> JsonValue {
|
||||
let mut json_data: JsonValue = object! {
|
||||
metrics: get_metrics(updates),
|
||||
images: object! {}
|
||||
};
|
||||
/// Takes a slice of `Image` objects and returns a `Value` with update info. The output doesn't contain much detail
|
||||
pub fn to_simple_json(updates: &[Image]) -> Value {
|
||||
let mut images = Map::new();
|
||||
updates.iter().for_each(|image| {
|
||||
let _ = json_data["images"].insert(&image.reference, image.has_update().to_option_bool());
|
||||
let _ = images.insert(
|
||||
image.reference.clone(),
|
||||
image.has_update().to_option_bool().into(),
|
||||
);
|
||||
});
|
||||
let json_data: Value = json!({
|
||||
"metrics": get_metrics(updates),
|
||||
"images": images,
|
||||
});
|
||||
json_data
|
||||
}
|
||||
|
||||
/// Takes a slice of `Image` objects and returns a `JsonValue` of update info. All image data is included, useful for debugging.
|
||||
pub fn to_full_json(updates: &[Image]) -> JsonValue {
|
||||
object! {
|
||||
metrics: get_metrics(updates),
|
||||
images: updates.iter().map(|image| image.to_json()).collect::<Vec<JsonValue>>(),
|
||||
}
|
||||
/// Takes a slice of `Image` objects and returns a `Value` with update info. All image data is included, useful for debugging.
|
||||
pub fn to_full_json(updates: &[Image]) -> Value {
|
||||
json!({
|
||||
"metrics": get_metrics(updates),
|
||||
"images": updates.iter().map(|image| image.to_json()).collect::<Vec<Value>>(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => ({
|
||||
eprintln!("\x1b[31;1mERROR \x1b[0m {}", format!($($arg)*));
|
||||
eprintln!("\x1b[31;1mERROR\x1b[0m {}", format!($($arg)*));
|
||||
std::process::exit(1);
|
||||
})
|
||||
}
|
||||
@@ -13,14 +13,14 @@ macro_rules! error {
|
||||
#[macro_export]
|
||||
macro_rules! warn {
|
||||
($($arg:tt)*) => ({
|
||||
eprintln!("\x1b[33;1mWARN \x1b[0m {}", format!($($arg)*));
|
||||
eprintln!("\x1b[33;1mWARN \x1b[0m {}", format!($($arg)*));
|
||||
})
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
($($arg:tt)*) => ({
|
||||
println!("\x1b[36;1mINFO \x1b[0m {}", format!($($arg)*));
|
||||
println!("\x1b[36;1mINFO \x1b[0m {}", format!($($arg)*));
|
||||
})
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ macro_rules! info {
|
||||
macro_rules! debug {
|
||||
($debg:expr, $($arg:tt)*) => ({
|
||||
if $debg {
|
||||
println!("\x1b[35;1mDEBUG \x1b[0m {}", format!($($arg)*));
|
||||
println!("\x1b[35;1mDEBUG\x1b[0m {}", format!($($arg)*));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// Miscellaneous utility functions that are too small to go in a separate file
|
||||
|
||||
use chrono::Local;
|
||||
|
||||
/// Gets the current timestamp. Mainly exists so I don't have to type this one line of code ;-)
|
||||
pub fn timestamp() -> i64 {
|
||||
Local::now().timestamp_millis()
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
pub mod json;
|
||||
pub mod link;
|
||||
pub mod logging;
|
||||
pub mod misc;
|
||||
pub mod reference;
|
||||
pub mod request;
|
||||
pub mod sort_update_vec;
|
||||
pub mod time;
|
||||
@@ -1,7 +1,7 @@
|
||||
use http_auth::parse_challenges;
|
||||
use json::JsonValue;
|
||||
use reqwest::Response;
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{config::RegistryConfig, error};
|
||||
|
||||
@@ -53,8 +53,8 @@ pub async fn get_response_body(response: Response) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_json(body: &str) -> JsonValue {
|
||||
match json::parse(body) {
|
||||
pub fn parse_json(body: &str) -> Value {
|
||||
match serde_json::from_str(body) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(e) => {
|
||||
error!("Failed to parse server response\n{}", e)
|
||||
|
||||
11
src/utils/time.rs
Normal file
11
src/utils/time.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
// When you're too bored to type some things, you get this...
|
||||
|
||||
use std::time::SystemTime;
|
||||
|
||||
pub fn elapsed(start: SystemTime) -> u32 {
|
||||
start.elapsed().unwrap().as_millis() as u32
|
||||
}
|
||||
|
||||
pub fn now() -> SystemTime {
|
||||
SystemTime::now()
|
||||
}
|
||||
Reference in New Issue
Block a user