Automatically generate slugs for articles based on title

This commit is contained in:
Magnus Hoff 2017-09-20 23:31:25 +02:00
parent ea28f4f4f7
commit debf44623c
4 changed files with 53 additions and 2 deletions

16
Cargo.lock generated
View file

@ -589,6 +589,7 @@ dependencies = [
"serde_derive 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"slug 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"static_resource_derive 0.1.0",
"tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -677,6 +678,14 @@ name = "slab"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "slug"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unidecode 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "smallvec"
version = "0.2.1"
@ -844,6 +853,11 @@ name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unidecode"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unreachable"
version = "1.0.0"
@ -994,6 +1008,7 @@ dependencies = [
"checksum serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce0fd303af908732989354c6f02e05e2e6d597152870f2c6990efb0577137480"
"checksum sha2 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d963c78ce367df26d7ea8b8cc655c651b42e8a1e584e869c1e17dae3ccb116a"
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
"checksum slug 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f5ff4b43cb07b86c5f9236c92714a22cdf9e5a27a7d85e398e2c9403328cb8"
"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013"
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
"checksum syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)" = "58fd09df59565db3399efbba34ba8a2fec1307511ebd245d0061ff9d42691673"
@ -1014,6 +1029,7 @@ dependencies = [
"checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f"
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum unidecode 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2adb95ee07cd579ed18131f2d9e7a17c25a4b76022935c7f2460d2bfae89fd2"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb819346883532a271eb626deb43c4a1bb4c4dd47c519bd78137c3e72a4fe27"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"

View file

@ -14,6 +14,7 @@ serde_derive = "1.0.0"
serde = "1.0.0"
serde_urlencoded = "0.5.0"
serde_json = "1.0"
slug = "0.1"
r2d2 = "0.7"
r2d2-diesel = "0.16"
regex = "0.2"

View file

@ -17,6 +17,7 @@ extern crate r2d2;
extern crate r2d2_diesel;
extern crate serde_json;
extern crate serde_urlencoded;
extern crate slug;
use std::net::SocketAddr;

View file

@ -26,6 +26,38 @@ pub enum SlugLookup {
Redirect(String),
}
fn decide_slug(conn: &SqliteConnection, prev_title: &str, title: &str, prev_slug: &str) -> Result<String, Error> {
if title == prev_title {
return Ok(prev_slug.to_owned());
}
let base_slug = ::slug::slugify(title);
if base_slug == prev_slug {
return Ok(base_slug);
}
use schema::article_revisions;
let mut slug = base_slug.clone();
let mut disambiguator = 1;
loop {
let slug_in_use = article_revisions::table
.filter(article_revisions::slug.eq(&slug))
.filter(article_revisions::latest.eq(true))
.count()
.first::<i64>(conn)? != 0;
if !slug_in_use {
break Ok(slug);
}
disambiguator += 1;
slug = format!("{}-{}", base_slug, disambiguator);
}
}
impl State {
pub fn new(connection_pool: Pool<ConnectionManager<SqliteConnection>>, cpu_pool: futures_cpupool::CpuPool) -> State {
State {
@ -106,8 +138,7 @@ impl State {
conn.transaction(|| {
use schema::article_revisions;
// TODO: Get title and slug as parameters to update_article, so we can... update those
let (latest_revision, title, slug) = article_revisions::table
let (latest_revision, prev_title, prev_slug) = article_revisions::table
.filter(article_revisions::article_id.eq(article_id))
.order(article_revisions::revision.desc())
.limit(1)
@ -127,6 +158,8 @@ impl State {
}
let new_revision = base_revision + 1;
let title = prev_title.clone(); // TODO Have title be a parameter to this function
let slug = decide_slug(&*conn, &prev_title, &title, &prev_slug)?;
#[derive(Insertable)]
#[table_name="article_revisions"]