consolidate shared helpers into utils crate

This commit is contained in:
2026-10-03 11:00:59 +07:00
parent 24070cc2cd
commit d00ee33eab
16 changed files with 89 additions and 177 deletions
+5 -1
View File
@@ -1,5 +1,9 @@
mod pubkey;
mod repo_url;
mod text;
mod time;
pub use pubkey::shorten_pubkey;
pub use time::{relative_time, relative_time_secs};
pub use repo_url::same_repo_url;
pub use text::{flatten_whitespace, middle_truncate};
pub use time::{latest, relative_time, relative_time_secs, sort_newest_first, sort_oldest_first};
+6 -41
View File
@@ -1,49 +1,14 @@
use nostr::prelude::*;
/// Shorten a [`PublicKey`] to `npub1abc...wxyz` form.
use crate::text::middle_truncate;
const HEAD_CHARS: usize = 9;
const TAIL_CHARS: usize = 4;
pub fn shorten_pubkey(public_key: PublicKey) -> String {
let encoded = public_key
.to_bech32()
.unwrap_or_else(|_| public_key.to_hex());
truncate_middle(&encoded)
}
fn truncate_middle(value: &str) -> String {
const HEAD_CHARS: usize = 9;
const TAIL_CHARS: usize = 4;
let length = value.chars().count();
if length <= HEAD_CHARS + TAIL_CHARS + 3 {
return value.to_owned();
}
let head: String = value.chars().take(HEAD_CHARS).collect();
let tail: String = value.chars().skip(length - TAIL_CHARS).collect();
format!("{head}...{tail}")
}
#[cfg(test)]
mod tests {
use super::*;
const PUBLIC_KEY_HEX: &str = "68d81165918100b7da43fc28f7d1fc12554466e1115886b9e7bb326f65ec4272";
#[test]
fn shortens_a_valid_pubkey() {
let public_key = PublicKey::from_hex(PUBLIC_KEY_HEX).expect("valid pubkey");
let npub = public_key.to_bech32().expect("valid pubkey encodes");
assert_eq!(
shorten_pubkey(public_key),
format!("{}...{}", &npub[..9], &npub[npub.len() - 4..])
);
}
#[test]
fn leaves_short_values_intact() {
assert_eq!(truncate_middle("npub1short"), "npub1short");
assert_eq!(truncate_middle("thirteenchars"), "thirteenchars");
}
middle_truncate(&encoded, HEAD_CHARS, TAIL_CHARS)
}
+20
View File
@@ -0,0 +1,20 @@
use nostr::prelude::*;
/// Compare git URLs ignoring scheme, trailing `.git` and path slashes, so a
/// grasp announce URL matches its https origin. Unparseable values compare literally.
pub fn same_repo_url(a: &str, b: &str) -> bool {
match (url_identity(a), url_identity(b)) {
(Some(a), Some(b)) => a == b,
_ => a == b,
}
}
fn url_identity(url: &str) -> Option<(String, Option<u16>, String)> {
let parsed = Url::parse(url).ok()?;
let host = parsed.host_str()?.to_ascii_lowercase();
let mut path = parsed.path().trim_matches('/').to_owned();
if let Some(stripped) = path.strip_suffix(".git") {
path = stripped.to_owned();
}
Some((host, parsed.port(), path))
}
+21
View File
@@ -0,0 +1,21 @@
pub fn middle_truncate(value: &str, head: usize, tail: usize) -> String {
let len = value.chars().count();
if len <= head + tail + 3 {
return value.to_string();
}
let head: String = value.chars().take(head).collect();
let tail: String = value.chars().skip(len - tail).collect();
format!("{head}...{tail}")
}
pub fn flatten_whitespace(text: &str) -> String {
const MAX_CHARS: usize = 200;
let flat = text.split_whitespace().collect::<Vec<_>>().join(" ");
if flat.chars().count() <= MAX_CHARS {
flat
} else {
let mut clipped: String = flat.chars().take(MAX_CHARS).collect();
clipped.push('…');
clipped
}
}
+19 -20
View File
@@ -1,6 +1,24 @@
use nostr::prelude::*;
/// Format a timestamp as a short relative time, e.g. `3h ago`.
pub fn sort_newest_first(events: &mut [Event]) {
events.sort_by(|a, b| {
b.created_at
.cmp(&a.created_at)
.then_with(|| b.id.to_hex().cmp(&a.id.to_hex()))
});
}
pub fn sort_oldest_first(events: &mut [Event]) {
events.sort_by_key(|e| e.created_at);
}
pub fn latest<I>(events: I) -> Option<Event>
where
I: IntoIterator<Item = Event>,
{
events.into_iter().max_by_key(|e| e.created_at)
}
pub fn relative_time(timestamp: Timestamp) -> String {
let now = Timestamp::now().as_secs();
let secs = now.saturating_sub(timestamp.as_secs());
@@ -20,25 +38,6 @@ pub fn relative_time(timestamp: Timestamp) -> String {
}
}
/// Format a unix timestamp in seconds as a short relative time, e.g. `3h ago`.
pub fn relative_time_secs(secs: i64) -> String {
relative_time(Timestamp::from_secs(secs.max(0) as u64))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn formats_relative_time() {
let now = Timestamp::now();
assert_eq!(relative_time(now), "just now");
assert_eq!(relative_time(now - 300), "5m ago");
assert_eq!(relative_time(now - 7_200), "2h ago");
assert_eq!(relative_time(now - 3 * 86_400), "3d ago");
assert_eq!(relative_time(now - 60 * 86_400), "2mo ago");
assert_eq!(relative_time(now - 800 * 86_400), "2y ago");
assert_eq!(relative_time(now + 600), "just now");
}
}