summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKornel <kornel@geekhood.net>2020-03-03 12:37:27 +0000
committerKornel <kornel@geekhood.net>2020-03-10 00:43:41 +0000
commitc516d4775a9d1db845db69e3917c371f894c1e50 (patch)
tree475d31da6fda0ce059b9e9e0b0372ead285735c9
parent68044807289ac950b94fa0d02c58d7531b127863 (diff)
author
-rw-r--r--crate_db/src/lib_crate_db.rs22
-rw-r--r--front_end/src/author_page.rs24
-rw-r--r--front_end/templates/author.rs.html37
-rw-r--r--kitchen_sink/src/lib_kitchen_sink.rs9
-rw-r--r--server/src/main.rs13
5 files changed, 84 insertions, 21 deletions
diff --git a/crate_db/src/lib_crate_db.rs b/crate_db/src/lib_crate_db.rs
index 4ddad18..9f0dd01 100644
--- a/crate_db/src/lib_crate_db.rs
+++ b/crate_db/src/lib_crate_db.rs
@@ -645,21 +645,24 @@ impl CrateDb {
pub async fn crates_of_author(&self, github_id: u32) -> FResult<Vec<CrateOwnerRow>> {
self.with_read("crates_of_author", |conn| {
- let mut query = conn.prepare_cached(r#"SELECT ac.crate_id, ac.invited_by_github_id, ac.invited_at, max(cv.created)
- FROM author_crates ac JOIN crate_versions cv USING(crate_id)
+ let mut query = conn.prepare_cached(r#"SELECT c.origin, ac.invited_by_github_id, ac.invited_at, max(cv.created)
+ FROM author_crates ac
+ JOIN crate_versions cv USING(crate_id)
+ JOIN crates c ON c.id = ac.crate_id
WHERE ac.github_id = ?1
GROUP BY ac.crate_id
+ LIMIT 2000
"#)?;
let q = query.query_map(&[&github_id], |row| {
- let crate_id: u32 = row.get_unwrap(0);
+ let origin = Origin::from_str(row.get_raw(0).as_str().unwrap());
let invited_by_github_id: Option<u32> = row.get_unwrap(1);
let invited_at = row.get_raw(2).as_str().ok().and_then(|d| DateTime::parse_from_rfc3339(d).ok());
let latest_timestamp: u32 = row.get_unwrap(3);
Ok(CrateOwnerRow {
- crate_id,
+ origin,
invited_by_github_id,
invited_at,
- latest_version: DateTime::from_utc(NaiveDateTime::from_timestamp(latest_timestamp as _, 0), FixedOffset::east(0)),
+ latest_release: DateTime::from_utc(NaiveDateTime::from_timestamp(latest_timestamp as _, 0), FixedOffset::east(0)),
})
})?;
Ok(q.filter_map(|x| x.ok()).collect())
@@ -1142,11 +1145,12 @@ impl KeywordInsert {
}
}
+#[derive(Debug)]
pub struct CrateOwnerRow {
- crate_id: u32,
- invited_by_github_id: Option<u32>,
- invited_at: Option<DateTime<FixedOffset>>,
- latest_version: DateTime<FixedOffset>,
+ pub origin: Origin,
+ pub invited_by_github_id: Option<u32>,
+ pub invited_at: Option<DateTime<FixedOffset>>,
+ pub latest_release: DateTime<FixedOffset>,
}
#[inline]
diff --git a/front_end/src/author_page.rs b/front_end/src/author_page.rs
index bf3a548..5bb2ae2 100644
--- a/front_end/src/author_page.rs
+++ b/front_end/src/author_page.rs
@@ -1,10 +1,15 @@
-use crate::templates;
use crate::Page;
+use crate::templates;
+use futures::stream::StreamExt;
+use kitchen_sink::CrateOwnerRow;
use kitchen_sink::CResult;
use kitchen_sink::KitchenSink;
use kitchen_sink::RichAuthor;
+use kitchen_sink::RichCrateVersion;
+use kitchen_sink::UserOrg;
use kitchen_sink::UserType;
use render_readme::Renderer;
+use std::sync::Arc;
// pub struct User {
// pub id: u32,
@@ -23,17 +28,32 @@ pub struct AuthorPage<'a> {
pub aut: &'a RichAuthor,
pub kitchen_sink: &'a KitchenSink,
pub markup: &'a Renderer,
+ pub crates: Vec<(Arc<RichCrateVersion>, CrateOwnerRow)>,
+ pub orgs: Vec<UserOrg>,
}
impl<'a> AuthorPage<'a> {
pub async fn new(aut: &'a RichAuthor, kitchen_sink: &'a KitchenSink, markup: &'a Renderer) -> CResult<AuthorPage<'a>> {
dbg!(&aut);
- let crates = kitchen_sink.crates_of_author(aut).await?;
+ let orgs = kitchen_sink.user_github_orgs(&aut.github.login).await?.unwrap_or_default();
+ let mut rows = kitchen_sink.crates_of_author(aut).await?;
+ rows.sort_by(|a,b| b.latest_release.cmp(&a.latest_release));
+ rows.truncate(200);
+ dbg!(&rows);
+
+ let crates = futures::stream::iter(rows.into_iter())
+ .filter_map(|row| async move {
+ let c = kitchen_sink.rich_crate_version_async(&row.origin).await.map_err(|e| eprintln!("{}", e)).ok()?;
+ Some((c, row))
+ })
+ .collect().await;
Ok(Self {
+ crates,
aut,
kitchen_sink,
markup,
+ orgs,
})
}
diff --git a/front_end/templates/author.rs.html b/front_end/templates/author.rs.html
index 244ddf2..bc95198 100644
--- a/front_end/templates/author.rs.html
+++ b/front_end/templates/author.rs.html
@@ -2,13 +2,13 @@
@use crate::Urler;
@use crate::AuthorPage;
-@(url: &Urler, c: &AuthorPage)
+@(url: &Urler, p: &AuthorPage)
-@:base(&c.page(), {
+@:base(&p.page(), {
<header id="author">
<div class="inner-col">
<div class="breadcrumbs">
- <h1><a href="/">Lib.rs</a></h1> › @if c.is_org() {
+ <h1><a href="/">Lib.rs</a></h1> › @if p.is_org() {
Orgs
} else {
Users
@@ -16,17 +16,42 @@
</div>
<h2>
- @"@"@c.login()
+ @"@"@p.login()
</h2>
<nav><ul>
- <li><a href="@c.github_url()">GitHub</a></li>
+ <li><a href="@p.github_url()">GitHub</a></li>
</ul></nav>
</div>
</header>
<main>
<div class="inner-col">
- Hi
+ @if !p.orgs.is_empty() {
+ <section>Member of GitHub orgs</section>
+ <ul>
+ @for org in &p.orgs {
+ <li><a href="@org.url">@login</a></li>
+ }
+ </ul>
+ }
+ <section>
+ <h3>Crates by @p.aut.name()</h3>
+ <ul class=crates-list>
+ @for (k, r) in &p.crates {
+ <li>
+ <a href="@url.krate(&k)">
+ <div class=h>
+ <h4>
+ @k.short_name()
+ </h4>
+ </div>
+ <div class=meta>
+ </div>
+ </a>
+ </li>
+ }
+ </ul>
+ </section>
</div>
</main>
</div>
diff --git a/kitchen_sink/src/lib_kitchen_sink.rs b/kitchen_sink/src/lib_kitchen_sink.rs
index b798bff..4d27ad6 100644
--- a/kitchen_sink/src/lib_kitchen_sink.rs
+++ b/kitchen_sink/src/lib_kitchen_sink.rs
@@ -2096,6 +2096,15 @@ pub struct RichAuthor {
pub github: User,
}
+impl RichAuthor {
+ pub fn name(&self) -> &str {
+ match &self.github.name {
+ Some(n) if !n.is_empty() => &n,
+ _ => &self.github.login,
+ }
+ }
+}
+
/// This is used to uniquely identify authors based on as little information as is available
#[derive(Debug, Hash, Eq, PartialEq)]
enum AuthorId {
diff --git a/server/src/main.rs b/server/src/main.rs
index cd38a34..05b5305 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -445,18 +445,23 @@ async fn handle_author(req: HttpRequest) -> Result<HttpResponse, ServerError> {
let login = req.match_info().query("author");
println!("author page for {:?}", login);
let state: &AServerState = req.app_data().expect("appdata");
- if !is_alnum(login) {
- return render_404_page(state, login);
+ let crates = state.crates.load();
+ let aut = match crates.author_by_login(&login).await {
+ Ok(aut) => aut,
+ Err(_) => {
+ return render_404_page(state, login, "user");
+ }
+ };
+ if aut.github.login != login {
+ return Ok(HttpResponse::PermanentRedirect().header("Location", format!("/~{}", encode(&aut.github.login))).body(""));
}
let cache_file = state.page_cache_dir.join(format!("@{}.html", login));
Ok(serve_cached(
with_file_cache(state, cache_file, 3600, {
- let login = login.to_owned();
let state = state.clone();
run_timeout(60, async move {
let crates = state.crates.load();
let mut page: Vec<u8> = Vec::with_capacity(32000);
- let aut = crates.author_by_login(&login).await?;
front_end::render_author_page(&mut page, &aut, &crates, &state.markup).await?;
Ok::<_, failure::Error>((page, None))
})