Added some basic logging.

Fix cache invalidation (1 day for now).
This commit is contained in:
2019-01-17 11:53:56 +01:00
parent a3e515c3a7
commit d056b52e71
10 changed files with 171 additions and 57 deletions

View File

@@ -1,12 +1,15 @@
use std::fs;
use std::io;
use chrono::prelude::*;
use chrono::Duration;
use directories::ProjectDirs;
use log::debug;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct Cache {
timestamp: u64,
timestamp: DateTime<Utc>,
pub data: Vec<u8>,
}
@@ -25,14 +28,37 @@ impl Context {
let cache = self.dirs.cache_dir().join(format!("{}-{}.bin", bin, key));
if cache.exists() {
debug!("cache: bin={} key={} path={:?} exists", bin, key, cache);
fs::File::open(cache)
.and_then(|file| {
bincode::deserialize_from(&file).map_err(|_| {
debug!("cache: bin={} key={} faild to deserialize", bin, key);
io::Error::new(io::ErrorKind::Other, "failed to deserialize cache entry")
})
})
.and_then(|cache: Cache| {
if cache.timestamp > Utc::now() {
debug!("cache: bin={} key={} ok", bin, key);
Ok(cache)
} else {
debug!(
"cache: bin={} key={} outdated ({})",
bin, key, cache.timestamp
);
Err(io::Error::new(
io::ErrorKind::Other,
"failed to deserialize cache entry",
))
}
})
.ok()
} else {
debug!("cache: bin={} key={} don't exists", bin, key);
None
}
}
@@ -42,7 +68,7 @@ impl Context {
D: AsRef<[u8]>,
{
let entry = Cache {
timestamp: 0,
timestamp: Utc::now() + Duration::days(1),
data: data.as_ref().to_vec(),
};
@@ -54,6 +80,11 @@ impl Context {
let cache = cache.join(format!("{}-{}.bin", bin, key));
debug!(
"cache: save: bin={} key={} path={:?} timestamp={}",
bin, key, cache, entry.timestamp
);
fs::OpenOptions::new()
.create(true)
.write(true)

View File

@@ -1,3 +1,5 @@
use std::process::Command;
use structopt::StructOpt;
mod context;
@@ -9,12 +11,35 @@ use crate::probe::*;
#[derive(Debug, StructOpt)]
#[structopt(name = "whoareyou", about = "Search for swedish phone numbers.")]
struct Opt {
#[structopt(short = "o", long = "open")]
open: bool,
number: String,
}
fn main() {
let opt = Opt::from_args();
fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"{}[{}][{}] {}",
chrono::Local::now().format("[%Y-%m-%d %H:%M:%S]"),
record.target(),
record.level(),
message
))
})
.level(log::LevelFilter::Off)
.level_for("reqwest", log::LevelFilter::Off)
.level_for("hyper", log::LevelFilter::Off)
.level_for("tokio_reactor", log::LevelFilter::Off)
.level_for("html5ever", log::LevelFilter::Off)
.level_for("selectors", log::LevelFilter::Off)
.chain(std::io::stdout())
.apply()
.expect("failed to init fern");
let mut probes: Vec<Box<Probe>> = vec![
Box::new(Eniro),
Box::new(Hitta),
@@ -23,11 +48,22 @@ fn main() {
Box::new(VemRingde),
];
let mut ctx = Context::new();
if opt.open {
for probe in &mut probes {
let uri = probe.uri(&opt.number);
for probe in &mut probes {
if probe.search(&mut ctx, &opt.number).is_ok() {
println!();
Command::new("open")
.arg(uri)
.output()
.expect("failed to execute process");
}
} else {
let mut ctx = Context::new();
for probe in &mut probes {
if probe.search(&mut ctx, &opt.number).is_ok() {
println!();
}
}
}
}

View File

@@ -13,5 +13,6 @@ pub use self::vem_ringde::VemRingde;
use crate::context::Context;
pub trait Probe {
fn uri(&self, _: &str) -> String;
fn search(&mut self, _: &mut Context, _: &str) -> Result<(), ()>;
}

View File

@@ -5,32 +5,34 @@ use crate::context::Context;
use crate::probe::Probe;
#[derive(Debug, FromHtml)]
#[html(selector = ".error-box")]
#[html(selector = ".PhoneNoHit")]
struct Error {
#[html(selector = "h2", attr = "inner")]
#[html(selector = ".search-info-container > p", attr = "inner")]
message: String,
}
// https://gulasidorna.eniro.se/hitta:{}
pub struct Eniro;
impl Probe for Eniro {
fn uri(&self, number: &str) -> String {
format!("https://gulasidorna.eniro.se/hitta:{}", number)
}
fn search(&mut self, ctx: &mut Context, number: &str) -> Result<(), ()> {
let body = if let Some(cache) = ctx.cache_get("eniro", &number) {
String::from_utf8(cache.data).unwrap()
} else {
reqwest::get(&format!("https://gulasidorna.eniro.se/hitta:{}", number))
.unwrap()
.text()
.unwrap()
};
let body = reqwest::get(&self.uri(number)).unwrap().text().unwrap();
ctx.cache_set("eniro", &number, body.as_bytes())
.expect("wut?! why not?!");
ctx.cache_set("eniro", &number, body.as_bytes())
.expect("wut?! why not?!");
body
};
if let Ok(error) = Error::from_html(&body) {
println!("eniro.se:");
println!(" {}", error.message);
println!(" Antal sökningar på det här numret: {}", error.message);
Ok(())
} else {

View File

@@ -39,22 +39,24 @@ struct Comment {
timestamp: u64,
}
// https://www.hitta.se/vem-ringde/{}
pub struct Hitta;
impl Probe for Hitta {
fn uri(&self, number: &str) -> String {
format!("https://www.hitta.se/vem-ringde/{}", number)
}
fn search(&mut self, ctx: &mut Context, number: &str) -> Result<(), ()> {
let body = if let Some(cache) = ctx.cache_get("hitta", &number) {
String::from_utf8(cache.data).unwrap()
} else {
reqwest::get(&format!("https://www.hitta.se/vem-ringde/{}", number))
.unwrap()
.text()
.unwrap()
};
let body = reqwest::get(&self.uri(number)).unwrap().text().unwrap();
ctx.cache_set("hitta", &number, body.as_bytes())
.expect("wut?! why not?!");
ctx.cache_set("hitta", &number, body.as_bytes())
.expect("wut?! why not?!");
body
};
let re = Regex::new(r#"<script>__NEXT_DATA__ = (.*?);__NEXT_LOADED_PAGES__"#).unwrap();

View File

@@ -11,25 +11,24 @@ struct Info {
message: String,
}
// http://konsumentinfo.se/telefonnummer/sverige/{}
pub struct KonsumentInfo;
impl Probe for KonsumentInfo {
fn uri(&self, number: &str) -> String {
format!("http://konsumentinfo.se/telefonnummer/sverige/{}", number)
}
fn search(&mut self, ctx: &mut Context, number: &str) -> Result<(), ()> {
let body = if let Some(cache) = ctx.cache_get("konsument_info", &number) {
String::from_utf8(cache.data).unwrap()
} else {
reqwest::get(&format!(
"http://konsumentinfo.se/telefonnummer/sverige/{}",
number
))
.unwrap()
.text()
.unwrap()
};
let body = reqwest::get(&self.uri(number)).unwrap().text().unwrap();
ctx.cache_set("konsument_info", &number, body.as_bytes())
.expect("wut?! why not?!");
ctx.cache_set("konsument_info", &number, body.as_bytes())
.expect("wut?! why not?!");
body
};
println!("konsumentinfo.se:");

View File

@@ -11,25 +11,24 @@ struct Info {
message: String,
}
// http://www.telefonforsaljare.nu/telefonnummer/{}/
pub struct Telefonforsaljare;
impl Probe for Telefonforsaljare {
fn uri(&self, number: &str) -> String {
format!("http://www.telefonforsaljare.nu/telefonnummer/{}/", number)
}
fn search(&mut self, ctx: &mut Context, number: &str) -> Result<(), ()> {
let body = if let Some(cache) = ctx.cache_get("telefonforsaljare", &number) {
String::from_utf8(cache.data).unwrap()
} else {
reqwest::get(&format!(
"http://www.telefonforsaljare.nu/telefonnummer/{}/",
number
))
.unwrap()
.text()
.unwrap()
};
let body = reqwest::get(&self.uri(number)).unwrap().text().unwrap();
ctx.cache_set("telefonforsaljare", &number, body.as_bytes())
.expect("wut?! why not?!");
ctx.cache_set("telefonforsaljare", &number, body.as_bytes())
.expect("wut?! why not?!");
body
};
println!("telefonforsaljare.nu:");

View File

@@ -1,22 +1,24 @@
use crate::context::Context;
use crate::probe::Probe;
// http://vemringde.se/?q={}
pub struct VemRingde;
impl Probe for VemRingde {
fn uri(&self, number: &str) -> String {
format!("http://vemringde.se/?q={}", number)
}
fn search(&mut self, ctx: &mut Context, number: &str) -> Result<(), ()> {
let body = if let Some(cache) = ctx.cache_get("vem_ringde", &number) {
let _body = if let Some(cache) = ctx.cache_get("vem_ringde", &number) {
String::from_utf8(cache.data).unwrap()
} else {
reqwest::get(&format!("http://vemringde.se/?q={}", number))
.unwrap()
.text()
.unwrap()
};
let body = reqwest::get(&self.uri(number)).unwrap().text().unwrap();
ctx.cache_set("vem_ringde", &number, body.as_bytes())
.expect("wut?! why not?!");
ctx.cache_set("vem_ringde", &number, body.as_bytes())
.expect("wut?! why not?!");
body
};
Err(())
}