diff --git a/Cargo.lock b/Cargo.lock
index 949d11e..90459d9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -339,7 +339,7 @@ dependencies = [
[[package]]
name = "cup"
-version = "2.4.0"
+version = "3.0.0"
dependencies = [
"bollard",
"chrono",
@@ -349,7 +349,6 @@ dependencies = [
"http-link",
"indicatif",
"itertools",
- "json",
"liquid",
"once_cell",
"regex",
@@ -848,12 +847,6 @@ dependencies = [
"wasm-bindgen",
]
-[[package]]
-name = "json"
-version = "0.12.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
-
[[package]]
name = "kstring"
version = "2.0.2"
@@ -981,16 +974,6 @@ dependencies = [
"windows-sys 0.48.0",
]
-[[package]]
-name = "nu-ansi-term"
-version = "0.46.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
-dependencies = [
- "overload",
- "winapi",
-]
-
[[package]]
name = "num-conv"
version = "0.1.0"
@@ -1037,12 +1020,6 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
-[[package]]
-name = "overload"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
-
[[package]]
name = "parking_lot"
version = "0.11.2"
@@ -1556,15 +1533,6 @@ dependencies = [
"digest",
]
-[[package]]
-name = "sharded-slab"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
-dependencies = [
- "lazy_static",
-]
-
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
@@ -1674,16 +1642,6 @@ dependencies = [
"syn",
]
-[[package]]
-name = "thread_local"
-version = "1.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
-dependencies = [
- "cfg-if",
- "once_cell",
-]
-
[[package]]
name = "time"
version = "0.3.36"
@@ -1839,32 +1797,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
- "valuable",
-]
-
-[[package]]
-name = "tracing-log"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
-dependencies = [
- "log",
- "once_cell",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-subscriber"
-version = "0.3.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
-dependencies = [
- "nu-ansi-term",
- "sharded-slab",
- "smallvec",
- "thread_local",
- "tracing-core",
- "tracing-log",
]
[[package]]
@@ -1941,12 +1873,6 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
-[[package]]
-name = "valuable"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
-
[[package]]
name = "version_check"
version = "0.9.4"
@@ -2347,8 +2273,6 @@ dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
- "tracing",
- "tracing-subscriber",
"xitca-http",
"xitca-server",
"xitca-service",
diff --git a/Cargo.toml b/Cargo.toml
index 107c3cd..0f5502a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,13 +1,13 @@
[package]
name = "cup"
-version = "2.4.0"
+version = "3.0.0"
edition = "2021"
[dependencies]
clap = { version = "4.5.7", features = ["derive"] }
indicatif = { version = "0.17.8", optional = true }
-tokio = { version = "1.38.0", features = ["macros"] }
-xitca-web = { version = "0.5.0", optional = true, features = ["logger"] }
+tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] }
+xitca-web = { version = "0.5.0", optional = true }
liquid = { version = "0.26.6", optional = true }
bollard = "0.16.1"
once_cell = "1.19.0"
@@ -15,7 +15,6 @@ http-auth = { version = "0.1.9", default-features = false, features = [] }
termsize = { version = "0.1.8", optional = true }
regex = { version = "1.10.5", default-features = false, features = ["perf"] }
chrono = { version = "0.4.38", default-features = false, features = ["std", "alloc", "clock"], optional = true }
-json = "0.12.4"
reqwest = { version = "0.12.7", default-features = false, features = ["rustls-tls"] }
futures = "0.3.30"
reqwest-retry = "0.6.1"
diff --git a/README.md b/README.md
index ff81d77..97f6a84 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ _If you like this project and/or use Cup, please consider starring the project
- Supports most registries, including Docker Hub, ghcr.io, Quay, lscr.io and even Gitea (or derivatives)
- Doesn't exhaust any rate limits. This is the original reason I created Cup. It was inspired by [What's up docker?](https://github.com/getwud/wud) which would always use it up.
- Beautiful CLI and web interface for checking on your containers any time.
-- The binary is tiny! At the time of writing it's just 5.2 MB. No more pulling 100+ MB docker images for a such a simple program.
+- The binary is tiny! At the time of writing it's just 5.1 MB. No more pulling 100+ MB docker images for a such a simple program.
- JSON output for both the CLI and web interface so you can connect Cup to integrations. It's easy to parse and makes webhooks and pretty dashboards simple to set up!
## Documentation 📘
diff --git a/docs/components/pages/Home.tsx b/docs/components/pages/Home.tsx
index 0cc4ea6..7c69fde 100644
--- a/docs/components/pages/Home.tsx
+++ b/docs/components/pages/Home.tsx
@@ -106,7 +106,7 @@ export function Home() {
{
- 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());
}
};
}
diff --git a/src/registry.rs b/src/registry.rs
index d713354..ea4286e 100644
--- a/src/registry.rs
+++ b/src/registry.rs
@@ -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,
})
diff --git a/src/server.rs b/src/server.rs
index 8d3f591..846fe3e 100644
--- a/src/server.rs
+++ b/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>>, path: PathRef<'_>)
async fn api_simple(data: StateRef<'_, Arc>>) -> 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>>) -> 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>>) -> WebResponse {
struct ServerData {
template: String,
raw_updates: Vec,
- 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::>();
- 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::>();
+ 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(next: &S, ctx: WebContext<'_, C, B>) -> Result>
+where
+ S: for<'r> Service, Response = WebResponse, Error = Error>,
+{
+ 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
+ )
+}
diff --git a/src/structs/image.rs b/src/structs/image.rs
index 24f395a..8e9936e 100644
--- a/src/structs/image.rs
+++ b/src/structs/image.rs
@@ -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,
pub version_info: Option,
pub error: Option,
- 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
diff --git a/src/utils/json.rs b/src/utils/json.rs
index 71d0d68..ca27c01 100644
--- a/src/utils/json.rs
+++ b/src/utils/json.rs
@@ -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::>(),
- }
+/// 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::>(),
+ })
}
diff --git a/src/utils/logging.rs b/src/utils/logging.rs
index 80463bc..5328227 100644
--- a/src/utils/logging.rs
+++ b/src/utils/logging.rs
@@ -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)*));
}
})
}
diff --git a/src/utils/misc.rs b/src/utils/misc.rs
deleted file mode 100644
index 2b31ad9..0000000
--- a/src/utils/misc.rs
+++ /dev/null
@@ -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()
-}
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index df18720..1e2f96c 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -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;
\ No newline at end of file
diff --git a/src/utils/request.rs b/src/utils/request.rs
index 180c74b..30c38d8 100644
--- a/src/utils/request.rs
+++ b/src/utils/request.rs
@@ -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)
diff --git a/src/utils/time.rs b/src/utils/time.rs
new file mode 100644
index 0000000..ce06d81
--- /dev/null
+++ b/src/utils/time.rs
@@ -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()
+}