keep existing session
This commit is contained in:
parent
05aa47d723
commit
2cd6446d13
1 changed files with 76 additions and 51 deletions
129
src/lib.rs
129
src/lib.rs
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue