mirror of
https://github.com/FliegendeWurst/inboxid.git
synced 2024-11-22 00:45:01 +00:00
Refactor maildir reading into lib
This commit is contained in:
parent
19347d3249
commit
a349925f78
41
Cargo.lock
generated
41
Cargo.lock
generated
@ -17,6 +17,12 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@ -265,6 +271,7 @@ dependencies = [
|
|||||||
name = "inboxid"
|
name = "inboxid"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"ascii_table",
|
"ascii_table",
|
||||||
"chrono",
|
"chrono",
|
||||||
"imap",
|
"imap",
|
||||||
@ -288,9 +295,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.49"
|
version = "0.3.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc15e39392125075f60c95ba416f5381ff6c3a948ff02ab12464715adf56c821"
|
checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
@ -316,9 +323,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.91"
|
version = "0.2.92"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7"
|
checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libsqlite3-sys"
|
name = "libsqlite3-sys"
|
||||||
@ -367,7 +374,7 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "moins"
|
name = "moins"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/FliegendeWurst/moins?branch=master#fd6d6ccebbe33effe49d13fd26ff2214f1655d41"
|
source = "git+https://github.com/FliegendeWurst/moins?branch=master#46df794709b7fdc23e83e61d97c75b11e8248f0f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"termion",
|
"termion",
|
||||||
]
|
]
|
||||||
@ -772,9 +779,9 @@ checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.72"
|
version = "0.2.73"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe"
|
checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"wasm-bindgen-macro",
|
"wasm-bindgen-macro",
|
||||||
@ -782,9 +789,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-backend"
|
name = "wasm-bindgen-backend"
|
||||||
version = "0.2.72"
|
version = "0.2.73"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3"
|
checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
@ -797,9 +804,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.72"
|
version = "0.2.73"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b"
|
checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
@ -807,9 +814,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.72"
|
version = "0.2.73"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d"
|
checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -820,15 +827,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.72"
|
version = "0.2.73"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa"
|
checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.49"
|
version = "0.3.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "59fe19d70f5dacc03f6e46777213facae5ac3801575d56ca6cbd4c93dcd12310"
|
checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
@ -19,3 +19,4 @@ chrono = "0.4.19"
|
|||||||
rusqlite = { git = "https://github.com/rusqlite/rusqlite", branch = "master", features = ["bundled"] }
|
rusqlite = { git = "https://github.com/rusqlite/rusqlite", branch = "master", features = ["bundled"] }
|
||||||
rustyline = "8.0.0"
|
rustyline = "8.0.0"
|
||||||
moins = { git = "https://github.com/FliegendeWurst/moins", branch = "master" }
|
moins = { git = "https://github.com/FliegendeWurst/moins", branch = "master" }
|
||||||
|
anyhow = "1.0.40"
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
use std::{collections::HashMap, env};
|
use std::{array::IntoIter, env};
|
||||||
|
|
||||||
use ascii_table::{Align, AsciiTable, Column};
|
use ascii_table::{Align, AsciiTable, Column};
|
||||||
use chrono::{Local, NaiveDateTime, TimeZone};
|
|
||||||
use inboxid::*;
|
use inboxid::*;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use mailparse::{MailHeaderMap, dateparse};
|
|
||||||
use rustyline::{Editor, error::ReadlineError};
|
use rustyline::{Editor, error::ReadlineError};
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
@ -19,27 +17,16 @@ fn main() -> Result<()> {
|
|||||||
fn show_listing(mailbox: &str) -> Result<()> {
|
fn show_listing(mailbox: &str) -> Result<()> {
|
||||||
let maildir = get_maildir(mailbox)?;
|
let maildir = get_maildir(mailbox)?;
|
||||||
|
|
||||||
|
let mut mails = Vec::new();
|
||||||
|
for x in maildir.list_cur() {
|
||||||
|
mails.push(x?);
|
||||||
|
}
|
||||||
|
let mut mails = maildir.get_mails(&mut mails)?;
|
||||||
|
mails.sort_by_key(|x| x.date);
|
||||||
|
|
||||||
let mut rows = Vec::new();
|
let mut rows = Vec::new();
|
||||||
let mut mail_list = Vec::new();
|
for (i, mail) in mails.iter().enumerate() {
|
||||||
let mut i = 0;
|
let flags = &mail.flags;
|
||||||
// TODO(refactor) merge with new
|
|
||||||
let mut list = maildir.list_cur_sorted(Box::new(|name| {
|
|
||||||
// sort by UID
|
|
||||||
name.splitn(2, '_').nth(1).map(|x| x.parse().unwrap_or(0)).unwrap_or(0)
|
|
||||||
})).collect_vec();
|
|
||||||
let list = list.iter_mut().map(
|
|
||||||
|x| x.as_mut().map(|x| (x.flags().to_owned(), x.id().to_owned(), x.parsed()))).collect_vec();
|
|
||||||
for maile in &list {
|
|
||||||
match maile {
|
|
||||||
Ok((flags, id, Ok(mail))) => {
|
|
||||||
let headers = mail.get_headers();
|
|
||||||
let from = headers.get_all_values("From").join(" ");
|
|
||||||
let subj = headers.get_all_values("Subject").join(" ");
|
|
||||||
let date = headers.get_all_values("Date").join(" ");
|
|
||||||
let date = dateparse(&date).map(|x| {
|
|
||||||
let dt = Local.from_utc_datetime(&NaiveDateTime::from_timestamp(x, 0));
|
|
||||||
dt.format("%Y-%m-%d %H:%M").to_string()
|
|
||||||
}).unwrap_or(date);
|
|
||||||
let mut flags_display = String::new();
|
let mut flags_display = String::new();
|
||||||
if flags.contains('F') {
|
if flags.contains('F') {
|
||||||
flags_display.push('+');
|
flags_display.push('+');
|
||||||
@ -52,24 +39,7 @@ fn show_listing(mailbox: &str) -> Result<()> {
|
|||||||
} else {
|
} else {
|
||||||
flags_display.push('*');
|
flags_display.push('*');
|
||||||
}
|
}
|
||||||
rows.push(vec![i.to_string(), flags_display, from, subj, date]);
|
rows.push(IntoIter::new([(mails.len() - i).to_string(), flags_display, mail.from.clone(), mail.subject.clone(), mail.date_iso.clone()]));
|
||||||
i += 1;
|
|
||||||
mail_list.push((flags, id, mail));
|
|
||||||
}
|
|
||||||
Ok((_, _, Err(e))) => {
|
|
||||||
println!("error parsing mail: {:?}", e);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("error: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rows.sort_unstable_by(|x, y| x[4].cmp(&y[4]));
|
|
||||||
let count = rows.len();
|
|
||||||
let mut mails = HashMap::new();
|
|
||||||
for (i, row) in rows.iter_mut().enumerate() {
|
|
||||||
mails.insert(count - i, &mail_list[row[0].parse::<usize>().unwrap()]);
|
|
||||||
row[0] = (count - i).to_string();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ascii_table = AsciiTable::default();
|
let mut ascii_table = AsciiTable::default();
|
||||||
@ -98,9 +68,10 @@ fn show_listing(mailbox: &str) -> Result<()> {
|
|||||||
let readline = rl.readline(">> ");
|
let readline = rl.readline(">> ");
|
||||||
match readline {
|
match readline {
|
||||||
Ok(line) => {
|
Ok(line) => {
|
||||||
let mail = &mails[&line.trim().parse::<usize>().unwrap()];
|
let idx = mails.len() - line.trim().parse::<usize>().unwrap();
|
||||||
println!("{}", std::str::from_utf8(&mail.2.get_body_raw().unwrap()).unwrap());
|
let mail = &mails[idx];
|
||||||
for x in &mail.2.subparts {
|
println!("{}", std::str::from_utf8(&mail.get_body_raw().unwrap()).unwrap());
|
||||||
|
for x in &mail.subparts {
|
||||||
if x.ctype.mimetype == "text/html" {
|
if x.ctype.mimetype == "text/html" {
|
||||||
continue; // TODO
|
continue; // TODO
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
use std::env;
|
use std::{array::IntoIter, env};
|
||||||
|
|
||||||
use ascii_table::{Align, AsciiTable, Column};
|
use ascii_table::{Align, AsciiTable, Column};
|
||||||
use chrono::{Local, NaiveDateTime, TimeZone};
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use mailparse::{MailHeaderMap, dateparse};
|
|
||||||
|
|
||||||
use inboxid::*;
|
use inboxid::*;
|
||||||
|
|
||||||
@ -19,32 +17,16 @@ fn main() -> Result<()> {
|
|||||||
fn show_listing(mailbox: &str) -> Result<()> {
|
fn show_listing(mailbox: &str) -> Result<()> {
|
||||||
let maildir = get_maildir(mailbox)?;
|
let maildir = get_maildir(mailbox)?;
|
||||||
|
|
||||||
|
let mut mails = Vec::new();
|
||||||
|
for x in maildir.list_new() {
|
||||||
|
mails.push(x?);
|
||||||
|
}
|
||||||
|
let mut mails = maildir.get_mails(&mut mails)?;
|
||||||
|
mails.sort_by_key(|x| x.id);
|
||||||
|
|
||||||
let mut rows = Vec::new();
|
let mut rows = Vec::new();
|
||||||
let mut seen = Vec::new();
|
for mail in &mails {
|
||||||
for mut maile in maildir.list_new_sorted(Box::new(|name| {
|
rows.push(IntoIter::new([mail.from.clone(), mail.subject.clone(), mail.date_iso.clone()]));
|
||||||
// sort by UID
|
|
||||||
u32::MAX - name.splitn(2, '_').nth(1).map(|x| x.parse().unwrap_or(0)).unwrap_or(0)
|
|
||||||
})) {
|
|
||||||
match maile.as_mut().map(|x| x.parsed()) {
|
|
||||||
Ok(Ok(mail)) => {
|
|
||||||
let headers = mail.get_headers();
|
|
||||||
let from = headers.get_all_values("From").join(" ");
|
|
||||||
let subj = headers.get_all_values("Subject").join(" ");
|
|
||||||
let date = headers.get_all_values("Date").join(" ");
|
|
||||||
let date = dateparse(&date).map(|x| {
|
|
||||||
let dt = Local.from_utc_datetime(&NaiveDateTime::from_timestamp(x, 0));
|
|
||||||
dt.format("%Y-%m-%d %H:%M").to_string()
|
|
||||||
}).unwrap_or(date);
|
|
||||||
rows.push(vec![from, subj, date]);
|
|
||||||
seen.push(maile.as_ref().unwrap().id().to_owned());
|
|
||||||
}
|
|
||||||
Ok(Err(e)) => {
|
|
||||||
println!("error parsing mail: {:?}", e);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("error: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ascii_table = AsciiTable::default();
|
let mut ascii_table = AsciiTable::default();
|
||||||
@ -64,8 +46,8 @@ fn show_listing(mailbox: &str) -> Result<()> {
|
|||||||
ascii_table.print(rows); // prints a 0 if empty :)
|
ascii_table.print(rows); // prints a 0 if empty :)
|
||||||
|
|
||||||
// only after the user saw the new mail, move it out of 'new'
|
// only after the user saw the new mail, move it out of 'new'
|
||||||
for seen in seen {
|
for seen in mails {
|
||||||
maildir.move_new_to_cur(&seen)?;
|
maildir.move_new_to_cur(&seen.id.to_string())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
78
src/lib.rs
78
src/lib.rs
@ -1,7 +1,10 @@
|
|||||||
use std::{borrow::Cow, env, fs, io, net::TcpStream};
|
use std::{borrow::Cow, convert::{TryFrom, TryInto}, env, fs, io, net::TcpStream, ops::Deref};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use chrono::{DateTime, Local, NaiveDateTime, TimeZone};
|
||||||
use imap::{Session, types::Flag};
|
use imap::{Session, types::Flag};
|
||||||
use maildir::Maildir;
|
use maildir::{MailEntry, Maildir};
|
||||||
|
use mailparse::{MailHeaderMap, ParsedMail, dateparse};
|
||||||
use rusqlite::{Connection, params};
|
use rusqlite::{Connection, params};
|
||||||
use rustls_connector::{RustlsConnector, rustls::{ClientSession, StreamOwned}};
|
use rustls_connector::{RustlsConnector, rustls::{ClientSession, StreamOwned}};
|
||||||
|
|
||||||
@ -48,9 +51,54 @@ pub fn gen_id(uid_validity: u32, uid: u32) -> String {
|
|||||||
format!("{}_{}", uid_validity, uid)
|
format!("{}_{}", uid_validity, uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct MaildirID {
|
||||||
|
uid_validity: u32,
|
||||||
|
uid: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for MaildirID {
|
||||||
|
type Error = Box<dyn std::error::Error>;
|
||||||
|
|
||||||
|
fn try_from(id: &str) -> Result<Self> {
|
||||||
|
let mut parts = id.splitn(2, '_');
|
||||||
|
let uid_validity = parts.next().context("invalid ID")?.parse()?;
|
||||||
|
let uid = parts.next().context("invalid ID")?.parse()?;
|
||||||
|
Ok(MaildirID {
|
||||||
|
uid_validity,
|
||||||
|
uid,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for MaildirID {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
format!("{}_{}", self.uid_validity, self.uid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EasyMail<'a> {
|
||||||
|
pub mail: ParsedMail<'a>,
|
||||||
|
pub id: MaildirID,
|
||||||
|
pub flags: String,
|
||||||
|
pub from: String,
|
||||||
|
pub subject: String,
|
||||||
|
pub date: DateTime<Local>,
|
||||||
|
pub date_iso: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Deref for EasyMail<'a> {
|
||||||
|
type Target = ParsedMail<'a>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.mail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait MaildirExtension {
|
pub trait MaildirExtension {
|
||||||
fn get_file(&self, name: &str) -> std::result::Result<String, io::Error>;
|
fn get_file(&self, name: &str) -> std::result::Result<String, io::Error>;
|
||||||
fn save_file(&self, name: &str, content: &str) -> std::result::Result<(), io::Error>;
|
fn save_file(&self, name: &str, content: &str) -> std::result::Result<(), io::Error>;
|
||||||
|
fn get_mails<'a>(&self, entries: &'a mut [MailEntry]) -> Result<Vec<EasyMail<'a>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaildirExtension for Maildir {
|
impl MaildirExtension for Maildir {
|
||||||
@ -61,6 +109,32 @@ impl MaildirExtension for Maildir {
|
|||||||
fn save_file(&self, name: &str, content: &str) -> std::result::Result<(), io::Error> {
|
fn save_file(&self, name: &str, content: &str) -> std::result::Result<(), io::Error> {
|
||||||
fs::write(self.path().join(name), content)
|
fs::write(self.path().join(name), content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_mails<'a>(&self, entries: &'a mut [MailEntry]) -> Result<Vec<EasyMail<'a>>> {
|
||||||
|
let mut mails = Vec::new();
|
||||||
|
for maile in entries {
|
||||||
|
let id = maile.id().try_into()?;
|
||||||
|
let flags = maile.flags().to_owned();
|
||||||
|
let mail = maile.parsed()?;
|
||||||
|
let headers = mail.get_headers();
|
||||||
|
let from = headers.get_all_values("From").join(" ");
|
||||||
|
let subject = headers.get_all_values("Subject").join(" ");
|
||||||
|
let date = headers.get_all_values("Date").join(" ");
|
||||||
|
let date = dateparse(&date).map(|x|
|
||||||
|
Local.from_utc_datetime(&NaiveDateTime::from_timestamp(x, 0))
|
||||||
|
)?;
|
||||||
|
mails.push(EasyMail {
|
||||||
|
mail,
|
||||||
|
flags,
|
||||||
|
id,
|
||||||
|
from,
|
||||||
|
subject,
|
||||||
|
date_iso: date.format("%Y-%m-%d %H:%M").to_string(),
|
||||||
|
date,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(mails)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn store_i64(x: u64) -> i64 {
|
pub fn store_i64(x: u64) -> i64 {
|
||||||
|
Loading…
Reference in New Issue
Block a user