use crate::PeerId;
use anyhow::Result;
use chrono;
use std::fs;
use std::io::Write;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
pub struct PendingPeers {
path: PathBuf,
}
impl PendingPeers {
pub fn new() -> Result<Self> {
let path = dirs::home_dir()
.ok_or_else(|| anyhow::anyhow!("no home dir"))?
.join(".quicnet/pending_peers");
Ok(Self { path })
}
pub fn log(&self, peer_id: &PeerId, addr: &str) -> Result<()> {
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
let entry = format!(
"{} {} {} # first seen {}\n",
timestamp,
peer_id,
addr,
chrono::Local::now().format("%Y-%m-%d %H:%M:%S")
);
fs::create_dir_all(self.path.parent().unwrap())?;
fs::OpenOptions::new()
.create(true)
.append(true)
.open(&self.path)?
.write_all(entry.as_bytes())?;
Ok(())
}
pub fn list_recent(&self, hours: u64) -> Result<Vec<(PeerId, String, u64)>> {
if !self.path.exists() {
return Ok(vec![]);
}
let cutoff = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() - (hours * 3600);
let mut peers = Vec::new();
let mut seen = std::collections::HashSet::new();
for line in fs::read_to_string(&self.path)?.lines().rev() {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 3
&& let (Ok(timestamp), Ok(peer_id)) =
(parts[0].parse::<u64>(), parts[1].parse::<PeerId>())
&& timestamp >= cutoff && seen.insert(peer_id) {
peers.push((peer_id, parts[2].to_string(), timestamp));
}
}
Ok(peers)
}
}