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, is_changed: bool,
/// Store the index value in memory only when it is in use by the init options /// Store the index value in memory only when it is in use by the init options
has_name: bool, has_name: bool,
has_length: bool,
} }
impl Index { impl Index {
pub fn init(capacity: usize, has_name: bool) -> Self { pub fn init(capacity: usize, has_name: bool, has_length: bool) -> Self {
Self { Self {
index: HashMap::with_capacity(capacity), index: HashMap::with_capacity(capacity),
is_changed: false, is_changed: false,
has_name, has_name,
has_length,
} }
} }
@ -43,12 +45,22 @@ impl Index {
self.index.values().map(|i| i.node).sum::<u64>() 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 if self
.index .index
.insert( .insert(
infohash, 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() .is_none()
{ {

View file

@ -6,16 +6,18 @@ const NAME_MAX_LEN: usize = 125; // + 3 bytes for `...` offset @TODO optional
pub struct Value { pub struct Value {
pub time: DateTime<Utc>, pub time: DateTime<Utc>,
pub node: u64, 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>, name: Option<String>,
} }
impl Value { impl Value {
/// Create new `Self` with current timestamp /// 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 { Self {
time: Utc::now(), time: Utc::now(),
node, node,
length,
name: filter_name(name), name: filter_name(name),
} }
} }
@ -23,6 +25,10 @@ impl Value {
pub fn name(&self) -> Option<&String> { pub fn name(&self) -> Option<&String> {
self.name.as_ref() 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 /// Prevent unexpected memory usage on store values from unknown source

View file

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

View file

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