From debf44623c4a23186cc2cd74a19c1ceea1854bea Mon Sep 17 00:00:00 2001 From: Magnus Hoff Date: Wed, 20 Sep 2017 23:31:25 +0200 Subject: [PATCH] Automatically generate slugs for articles based on title --- Cargo.lock | 16 ++++++++++++++++ Cargo.toml | 1 + src/main.rs | 1 + src/state.rs | 37 +++++++++++++++++++++++++++++++++++-- 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d28956..7781619 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 2b6ee60..37c8c0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/main.rs b/src/main.rs index f1cd4bd..3f207cb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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; diff --git a/src/state.rs b/src/state.rs index 2410220..8eece77 100644 --- a/src/state.rs +++ b/src/state.rs @@ -26,6 +26,38 @@ pub enum SlugLookup { Redirect(String), } +fn decide_slug(conn: &SqliteConnection, prev_title: &str, title: &str, prev_slug: &str) -> Result { + 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::(conn)? != 0; + + if !slug_in_use { + break Ok(slug); + } + + disambiguator += 1; + slug = format!("{}-{}", base_slug, disambiguator); + } +} + impl State { pub fn new(connection_pool: Pool>, 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"]