keep existing session

This commit is contained in:
Shautvast 2026-02-17 20:03:20 +01:00
parent 05aa47d723
commit 2cd6446d13

View file

@ -1,7 +1,7 @@
use std::io::{Error, Stdout}; use std::io::{Error, Stdout};
use std::net::TcpStream; use std::net::TcpStream;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use crossterm::{event}; use crossterm::event;
use crossterm::event::{Event, KeyCode}; use crossterm::event::{Event, KeyCode};
use ratatui::backend::CrosstermBackend; use ratatui::backend::CrosstermBackend;
use ratatui::layout::{Constraint, Direction, Layout}; use ratatui::layout::{Constraint, Direction, Layout};
@ -14,20 +14,86 @@ pub mod config;
const POLL_INTERVAL: Duration = Duration::from_secs(30); const POLL_INTERVAL: Duration = Duration::from_secs(30);
pub(crate) struct Email { struct Email {
subject: String, subject: String,
from: String, from: String,
date: String, date: String,
} }
pub fn main(config: &Config, mut terminal: &mut Terminal<CrosstermBackend<Stdout>>) -> Result<(), Error>{ type ImapSession = imap::Session<TcpStream>;
let mut inbox = fetch_inbox(config);
fn connect(config: &Config) -> Result<ImapSession, String> {
let imap_cfg = &config.imap;
let stream =
TcpStream::connect((&*imap_cfg.host, imap_cfg.port)).map_err(|e| e.to_string())?;
let client = imap::Client::new(stream);
let session = client
.login(&imap_cfg.username, &imap_cfg.password)
.map_err(|(e, _)| e.to_string())?;
Ok(session)
}
fn fetch_inbox(session: &mut ImapSession) -> Result<Vec<Email>, String> {
session.select("INBOX").map_err(|e| e.to_string())?;
let messages = session
.fetch("1:*", "BODY.PEEK[HEADER.FIELDS (SUBJECT FROM DATE)]")
.map_err(|e| e.to_string())?;
let mut emails = Vec::new();
for message in messages.iter() {
if let Some(body) = message.header() {
let header = String::from_utf8_lossy(body);
let mut subject = String::new();
let mut from = String::new();
let mut date = String::new();
for line in header.lines() {
if let Some(val) = line.strip_prefix("Subject: ") {
subject = val.to_string();
} else if let Some(val) = line.strip_prefix("From: ") {
from = val.to_string();
} else if let Some(val) = line.strip_prefix("Date: ") {
date = val.to_string();
}
}
emails.push(Email { subject, from, date });
}
}
Ok(emails)
}
/// Refresh inbox using NOOP + fetch. Reconnects on error.
fn refresh_inbox(
session: &mut Option<ImapSession>,
config: &Config,
) -> Result<Vec<Email>, String> {
// If we have a session, try NOOP to keep alive / detect changes
if let Some(s) = session.as_mut() {
if s.noop().is_ok() {
return fetch_inbox(s);
}
}
// Session is dead or missing — reconnect
*session = None;
let mut new_session = connect(config)?;
let result = fetch_inbox(&mut new_session);
*session = Some(new_session);
result
}
pub fn main(config: &Config, terminal: &mut Terminal<CrosstermBackend<Stdout>>) -> Result<(), Error> {
let mut session = connect(config).ok();
let mut inbox = refresh_inbox(&mut session, config);
let mut last_fetch = Instant::now(); let mut last_fetch = Instant::now();
// --- Main loop --- // --- Main loop ---
loop { loop {
if last_fetch.elapsed() >= POLL_INTERVAL { if last_fetch.elapsed() >= POLL_INTERVAL {
inbox = fetch_inbox(&config); inbox = refresh_inbox(&mut session, config);
last_fetch = Instant::now(); last_fetch = Instant::now();
} }
@ -83,7 +149,7 @@ pub fn main(config: &Config, mut terminal: &mut Terminal<CrosstermBackend<Stdout
match key.code { match key.code {
KeyCode::Char('q') | KeyCode::Esc => break, KeyCode::Char('q') | KeyCode::Esc => break,
KeyCode::Char('r') => { KeyCode::Char('r') => {
inbox = fetch_inbox(&config); inbox = refresh_inbox(&mut session, config);
last_fetch = Instant::now(); last_fetch = Instant::now();
} }
_ => {} _ => {}
@ -92,51 +158,10 @@ pub fn main(config: &Config, mut terminal: &mut Terminal<CrosstermBackend<Stdout
} }
} }
// Clean up the session
if let Some(mut s) = session.take() {
let _ = s.logout();
}
Ok(()) Ok(())
} }
pub(crate) fn fetch_inbox(config: &Config) -> Result<Vec<Email>, String> {
let imap_cfg = &config.imap;
let stream =
TcpStream::connect((&*imap_cfg.host, imap_cfg.port)).map_err(|e| e.to_string())?;
let client = imap::Client::new(stream);
let mut session = client
.login(&imap_cfg.username, &imap_cfg.password)
.map_err(|(e, _)| e.to_string())?;
session.select("INBOX").map_err(|e| e.to_string())?;
let mut emails = Vec::new();
let messages = session
.fetch("1:*", "BODY.PEEK[HEADER.FIELDS (SUBJECT FROM DATE)]")
.map_err(|e| e.to_string())?;
for message in messages.iter() {
if let Some(body) = message.header() {
let header = String::from_utf8_lossy(body);
let mut subject = String::new();
let mut from = String::new();
let mut date = String::new();
for line in header.lines() {
if let Some(val) = line.strip_prefix("Subject: ") {
subject = val.to_string();
} else if let Some(val) = line.strip_prefix("From: ") {
from = val.to_string();
} else if let Some(val) = line.strip_prefix("Date: ") {
date = val.to_string();
}
}
emails.push(Email {
subject,
from,
date,
});
}
}
let _ = session.logout();
Ok(emails)
}