wip
parent
b97ab650ce
commit
9de95559d6
|
@ -34,7 +34,7 @@ struct ListsTemplate<'a> {
|
|||
title: &'a str,
|
||||
description: &'a str,
|
||||
lists_len: usize,
|
||||
lists: Vec<ListTemplate<'a>>,
|
||||
lists: Vec<DbVal<MailingList>>,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
|
@ -50,7 +50,12 @@ struct ListTemplate<'a> {
|
|||
impl<'a> Into<ListTemplate<'a>> for (&'a DbVal<MailingList>, &'a Database) {
|
||||
fn into(self: (&'a DbVal<MailingList>, &'a Database)) -> ListTemplate<'a> {
|
||||
let (list, db) = self;
|
||||
let months = db.months(list.pk).unwrap();
|
||||
let months = db
|
||||
.months(list.pk)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|s| s.replace('-', "/").to_string())
|
||||
.collect();
|
||||
let posts = db.list_posts(list.pk, None).unwrap();
|
||||
ListTemplate {
|
||||
title: &list.name,
|
||||
|
@ -86,7 +91,12 @@ async fn main() {
|
|||
let list_handler = warp::path!("lists" / i64).map(|list_pk: i64| {
|
||||
let db = Database::open_or_create_db().unwrap();
|
||||
let list = db.get_list(list_pk).unwrap().unwrap();
|
||||
let months = db.months(list_pk).unwrap();
|
||||
let months = db
|
||||
.months(list_pk)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|s| s.replace('-', "/").to_string())
|
||||
.collect();
|
||||
let posts = db.list_posts(list_pk, None).unwrap();
|
||||
let template = ListTemplate {
|
||||
title: &list.name,
|
||||
|
@ -98,6 +108,25 @@ async fn main() {
|
|||
let res = template.render().unwrap();
|
||||
Ok(warp::reply::html(res))
|
||||
});
|
||||
let month_handler =
|
||||
warp::path!("list" / i64 / i64 / i64).map(|list_pk: i64, year: i64, month: i64| {
|
||||
let db = Database::open_or_create_db().unwrap();
|
||||
let list = db.get_list(list_pk).unwrap().unwrap();
|
||||
let mut posts = db.list_posts(list_pk, Some((year, month))).unwrap();
|
||||
let template = ListTemplate {
|
||||
title: &list.name,
|
||||
list: &list,
|
||||
posts,
|
||||
months: vec![month.to_string()],
|
||||
body: &list
|
||||
.description
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
let res = template.render().unwrap();
|
||||
Ok(warp::reply::html(res))
|
||||
});
|
||||
let post_handler =
|
||||
warp::path!("list" / i64 / String).map(|list_pk: i64, message_id: String| {
|
||||
let message_id = percent_decode_str(&message_id).decode_utf8().unwrap();
|
||||
|
@ -133,15 +162,12 @@ async fn main() {
|
|||
let index_handler = warp::path::end().map(|| {
|
||||
let db = Database::open_or_create_db().unwrap();
|
||||
let lists_values = db.list_lists().unwrap();
|
||||
let lists = lists_values
|
||||
.iter()
|
||||
.map(|list| (list, &db).into())
|
||||
.collect::<Vec<ListTemplate<'_>>>();
|
||||
let lists = lists_values;
|
||||
let template = ListsTemplate {
|
||||
title: "mailing list archive",
|
||||
description: "",
|
||||
lists_len: lists.len(),
|
||||
lists: lists,
|
||||
lists,
|
||||
};
|
||||
let res = template.render().unwrap();
|
||||
Ok(warp::reply::html(res))
|
||||
|
@ -149,6 +175,7 @@ async fn main() {
|
|||
let routes = warp::get()
|
||||
.and(index_handler)
|
||||
.or(list_handler)
|
||||
.or(month_handler)
|
||||
.or(post_handler);
|
||||
|
||||
// Note that composing filters for many routes may increase compile times (because it uses a lot of generics).
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="entry">
|
||||
<h1>{{title}}</h1>
|
||||
<div class="body">
|
||||
{{body}}
|
||||
{{body | safe}}
|
||||
</div>
|
||||
</div>
|
||||
{% include "footer.html" %}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
{% include "header.html" %}
|
||||
<div class="entry">
|
||||
<h1>{{title}}</h1>
|
||||
<div class="body">
|
||||
{{body}}
|
||||
{{body|safe}}
|
||||
<h2>Months</h2>
|
||||
<ul>
|
||||
{% for month in months %}
|
||||
|
@ -17,4 +16,3 @@
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% include "footer.html" %}
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
<div class="entry">
|
||||
<p>{{lists_len}} lists</p>
|
||||
<div class="body">
|
||||
{% for list in lists %}
|
||||
{{ list }}
|
||||
<ul>
|
||||
{% for list in lists.as_slice() -%}
|
||||
<li><a href="/lists/{{ list.pk }}">{{ list }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% include "footer.html" %}
|
||||
|
|
|
@ -2,8 +2,35 @@
|
|||
<div class="entry">
|
||||
<h1>{{subject}}</h1>
|
||||
<div class="body">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>From</td>
|
||||
<td><code>{{_from}}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>To</td>
|
||||
<td><code>{{_to}}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Subject</td>
|
||||
<td>{{subject}}</td>
|
||||
</tr>
|
||||
{% if _in_reply_to.is_some() %}
|
||||
<tr>
|
||||
<td>In-­Reply-­To</td>
|
||||
<td><code>{{_in_reply_to.as_ref().unwrap() }}</code></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td>References</td>
|
||||
<td>{% for reference in _references %}<code>{{reference}}</code>, {% endfor %}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<pre>
|
||||
{{body}}
|
||||
{{body|safe}}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -45,8 +45,9 @@ fn main() {
|
|||
let exit = verify.wait_with_output().unwrap();
|
||||
if !exit.status.success() {
|
||||
panic!(
|
||||
"sqlite3 could not read SQL schema: {}",
|
||||
String::from_utf8_lossy(&exit.stdout)
|
||||
"sqlite3 could not read SQL schema: {}\n{}",
|
||||
String::from_utf8_lossy(&exit.stdout),
|
||||
String::from_utf8_lossy(&exit.stderr)
|
||||
);
|
||||
}
|
||||
let mut file = std::fs::File::create("./src/schema.sql").unwrap();
|
||||
|
|
|
@ -271,31 +271,32 @@ impl Database {
|
|||
pub fn list_posts(
|
||||
&self,
|
||||
list_pk: i64,
|
||||
_date_range: Option<(String, String)>,
|
||||
date_range: Option<(i64, i64)>,
|
||||
) -> Result<Vec<DbVal<Post>>> {
|
||||
let mut stmt = self
|
||||
.connection
|
||||
.prepare("SELECT * FROM post WHERE list = ?;")?;
|
||||
let iter = stmt.query_map(rusqlite::params![&list_pk,], |row| {
|
||||
let pk = row.get("pk")?;
|
||||
Ok(DbVal(
|
||||
Post {
|
||||
pk,
|
||||
list: row.get("list")?,
|
||||
address: row.get("address")?,
|
||||
message_id: row.get("message_id")?,
|
||||
message: row.get("message")?,
|
||||
timestamp: row.get("timestamp")?,
|
||||
datetime: row.get("datetime")?,
|
||||
},
|
||||
pk,
|
||||
))
|
||||
})?;
|
||||
let mut ret = vec![];
|
||||
for post in iter {
|
||||
let post = post?;
|
||||
ret.push(post);
|
||||
}
|
||||
if let Some((year, month)) = date_range.as_ref() {
|
||||
let mut stmt = self
|
||||
.connection
|
||||
.prepare("SELECT * FROM post WHERE list = ? AND year = ? AND month = ?;")?;
|
||||
let iter = stmt.query_map(rusqlite::params![list_pk, year, month], |row| {
|
||||
Ok(DbVal::<Post>::try_from(row)?)
|
||||
})?;
|
||||
for post in iter {
|
||||
let post = post?;
|
||||
ret.push(post);
|
||||
}
|
||||
} else {
|
||||
let mut stmt = self
|
||||
.connection
|
||||
.prepare("SELECT * FROM post WHERE list = ?;")?;
|
||||
let iter = stmt.query_map(rusqlite::params![list_pk,], |row| {
|
||||
Ok(DbVal::<Post>::try_from(row)?)
|
||||
})?;
|
||||
for post in iter {
|
||||
let post = post?;
|
||||
ret.push(post);
|
||||
}
|
||||
};
|
||||
|
||||
trace!("list_posts {:?}.", &ret);
|
||||
Ok(ret)
|
||||
|
|
|
@ -281,3 +281,22 @@ impl std::fmt::Display for Post {
|
|||
write!(fmt, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 's> std::convert::TryFrom<&'r rusqlite::Row<'s>> for DbVal<Post> {
|
||||
type Error = rusqlite::Error;
|
||||
fn try_from(row: &'r rusqlite::Row<'s>) -> std::result::Result<Self, Self::Error> {
|
||||
let pk = row.get("pk")?;
|
||||
Ok(DbVal(
|
||||
Post {
|
||||
pk,
|
||||
list: row.get("list")?,
|
||||
address: row.get("address")?,
|
||||
message_id: row.get("message_id")?,
|
||||
message: row.get("message")?,
|
||||
timestamp: row.get("timestamp")?,
|
||||
datetime: row.get("datetime")?,
|
||||
},
|
||||
pk,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,9 @@ CREATE TABLE IF NOT EXISTS post (
|
|||
message_id TEXT NOT NULL,
|
||||
message BLOB NOT NULL,
|
||||
timestamp INTEGER NOT NULL DEFAULT (unixepoch()),
|
||||
datetime TEXT NOT NULL DEFAULT (datetime())
|
||||
datetime TEXT NOT NULL DEFAULT (datetime()),
|
||||
year INTEGER AS (CAST(substr(datetime,0,5) AS INTEGER)) NOT NULL,
|
||||
month INTEGER AS (CAST(substr(datetime,6,7) AS INTEGER)) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS post_event (
|
||||
|
@ -97,3 +99,4 @@ CREATE INDEX IF NOT EXISTS post_listpk_idx ON post(list);
|
|||
CREATE INDEX IF NOT EXISTS post_msgid_idx ON post(message_id);
|
||||
CREATE INDEX IF NOT EXISTS mailing_lists_idx ON mailing_lists(id);
|
||||
CREATE INDEX IF NOT EXISTS membership_idx ON membership(address);
|
||||
CREATE INDEX IF NOT EXISTS post_date_idx ON post(year ASC, month ASC);
|
||||
|
|
|
@ -2,6 +2,7 @@ define(xor, `(($1) OR ($2)) AND NOT (($1) AND ($2))')dnl
|
|||
define(BOOLEAN_TYPE, `$1 BOOLEAN CHECK ($1 in (0, 1)) NOT NULL')dnl
|
||||
define(BOOLEAN_FALSE, `0')dnl
|
||||
define(BOOLEAN_TRUE, `1')dnl
|
||||
undefine(substr)dnl
|
||||
PRAGMA foreign_keys = true;
|
||||
PRAGMA encoding = 'UTF-8';
|
||||
|
||||
|
@ -74,7 +75,9 @@ CREATE TABLE IF NOT EXISTS post (
|
|||
message_id TEXT NOT NULL,
|
||||
message BLOB NOT NULL,
|
||||
timestamp INTEGER NOT NULL DEFAULT (unixepoch()),
|
||||
datetime TEXT NOT NULL DEFAULT (datetime())
|
||||
datetime TEXT NOT NULL DEFAULT (datetime()),
|
||||
year INTEGER AS (CAST(substr(datetime,0,5) AS INTEGER)) NOT NULL,
|
||||
month INTEGER AS (CAST(substr(datetime,6,7) AS INTEGER)) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS post_event (
|
||||
|
@ -101,3 +104,4 @@ CREATE INDEX IF NOT EXISTS post_listpk_idx ON post(list);
|
|||
CREATE INDEX IF NOT EXISTS post_msgid_idx ON post(message_id);
|
||||
CREATE INDEX IF NOT EXISTS mailing_lists_idx ON mailing_lists(id);
|
||||
CREATE INDEX IF NOT EXISTS membership_idx ON membership(address);
|
||||
CREATE INDEX IF NOT EXISTS post_date_idx ON post(year ASC, month ASC);
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue