show bytes as description, implement format bytes trait

This commit is contained in:
yggverse 2025-07-07 15:48:36 +03:00
parent d05b15c7a3
commit f49ed0e11b
5 changed files with 66 additions and 13 deletions

24
src/format.rs Normal file
View file

@ -0,0 +1,24 @@
pub trait Format {
/// Format bytes to KB/MB/GB presentation
fn bytes(self) -> String;
}
impl Format for u64 {
fn bytes(self) -> String {
const KB: f32 = 1024.0;
const MB: f32 = KB * KB;
const GB: f32 = MB * KB;
let f = self as f32;
if f < KB {
format!("{self} B")
} else if f < MB {
format!("{:.2} KB", f / KB)
} else if f < GB {
format!("{:.2} MB", f / MB)
} else {
format!("{:.2} GB", f / GB)
}
}
}

View file

@ -12,14 +12,16 @@ pub struct Index {
is_changed: bool,
/// Store the index value in memory only when it is in use by the init options
has_name: bool,
has_length: bool,
}
impl Index {
pub fn init(capacity: usize, has_name: bool) -> Self {
pub fn init(capacity: usize, has_name: bool, has_length: bool) -> Self {
Self {
index: HashMap::with_capacity(capacity),
is_changed: false,
has_name,
has_length,
}
}
@ -43,12 +45,22 @@ impl Index {
self.index.values().map(|i| i.node).sum::<u64>()
}
pub fn insert(&mut self, infohash: String, node: u64, name: Option<String>) {
pub fn insert(
&mut self,
infohash: String,
node: u64,
name: Option<String>,
length: Option<u64>,
) {
if self
.index
.insert(
infohash,
Value::new(node, if self.has_name { name } else { None }),
Value::new(
node,
if self.has_name { name } else { None },
if self.has_length { length } else { None },
),
)
.is_none()
{

View file

@ -6,16 +6,18 @@ const NAME_MAX_LEN: usize = 125; // + 3 bytes for `...` offset @TODO optional
pub struct Value {
pub time: DateTime<Utc>,
pub node: u64,
/// Isolate by applying internal filter on value set
// Isolate by applying internal filter on value set
length: Option<u64>,
name: Option<String>,
}
impl Value {
/// Create new `Self` with current timestamp
pub fn new(node: u64, name: Option<String>) -> Self {
pub fn new(node: u64, name: Option<String>, length: Option<u64>) -> Self {
Self {
time: Utc::now(),
node,
length,
name: filter_name(name),
}
}
@ -23,6 +25,10 @@ impl Value {
pub fn name(&self) -> Option<&String> {
self.name.as_ref()
}
/// Get reference to the safely constructed `length` member
pub fn length(&self) -> Option<u64> {
self.length
}
}
/// Prevent unexpected memory usage on store values from unknown source

View file

@ -1,6 +1,7 @@
mod api;
mod config;
mod debug;
mod format;
mod index;
mod peers;
mod preload;
@ -11,6 +12,7 @@ mod trackers;
use anyhow::Result;
use config::Config;
use debug::Debug;
use format::Format;
use index::Index;
use peers::Peers;
use rss::Rss;
@ -73,7 +75,11 @@ async fn main() -> Result<()> {
// begin
debug.info("Crawler started");
let mut index = Index::init(config.index_capacity, config.export_rss.is_some());
let mut index = Index::init(
config.index_capacity,
config.export_rss.is_some(),
config.export_rss.is_some(),
);
loop {
debug.info("Index queue begin...");
index.refresh();
@ -127,7 +133,7 @@ async fn main() -> Result<()> {
config.preload_max_filecount.unwrap_or_default(),
);
mt.wait_until_initialized().await?;
let name = mt.with_metadata(|m| {
let (name, length) = mt.with_metadata(|m| {
// init preload files list
if let Some(ref p) = preload {
for (id, info) in m.file_infos.iter().enumerate() {
@ -160,7 +166,7 @@ async fn main() -> Result<()> {
if let Some(ref t) = torrent {
save_torrent_file(t, &debug, &i, &m.torrent_bytes)
}
m.info.name.as_ref().map(|n|n.to_string())
(m.info.name.as_ref().map(|n|n.to_string()), m.info.length)
})?;
session.update_only_files(&mt, &only_files).await?;
session.unpause(&mt).await?;
@ -175,7 +181,7 @@ async fn main() -> Result<()> {
p.cleanup(&i, Some(only_files_keep))?
}
index.insert(i, only_files_size, name)
index.insert(i, only_files_size, name, length)
}
Ok(AddTorrentResponse::ListOnly(r)) => {
if let Some(ref t) = torrent {
@ -186,7 +192,12 @@ async fn main() -> Result<()> {
// use `r.info` for Memory, SQLite,
// Manticore and other alternative storage type
index.insert(i, 0, r.info.name.map(|n| n.to_string()))
index.insert(
i,
0,
r.info.name.map(|n| n.to_string()),
r.info.length,
)
}
// unexpected as should be deleted
Ok(AddTorrentResponse::AlreadyManaged(..)) => panic!(),
@ -213,7 +224,7 @@ async fn main() -> Result<()> {
rss.push(
k,
v.name().unwrap_or(k),
None, // @TODO
v.length().map(|l| l.bytes()),
Some(&v.time.to_rfc2822()),
)?
}

View file

@ -59,7 +59,7 @@ impl Rss {
&mut self,
infohash: &str,
title: &str,
description: Option<&str>,
description: Option<String>,
pub_date: Option<&str>,
) -> Result<()> {
self.file.write_all(
@ -72,7 +72,7 @@ impl Rss {
)?;
if let Some(s) = description {
self.file.write_all(b"<description>")?;
self.file.write_all(escape(s).as_bytes())?;
self.file.write_all(escape(&s).as_bytes())?;
self.file.write_all(b"</description>")?
}
if let Some(s) = pub_date {