Add links.rs for checking and building links

This commit is contained in:
RumovZ 2021-07-22 10:03:03 +02:00
parent b61e90e995
commit 80264c3b56
4 changed files with 409 additions and 2 deletions

286
Cargo.lock generated
View file

@ -72,6 +72,7 @@ dependencies = [
"intl-memoizer",
"itertools 0.10.1",
"lazy_static",
"linkcheck",
"nom 7.0.0-alpha1",
"num-integer",
"num_enum",
@ -84,7 +85,7 @@ dependencies = [
"pulldown-cmark",
"rand 0.8.4",
"regex",
"reqwest",
"reqwest 0.11.3",
"rusqlite",
"scopeguard",
"serde",
@ -337,12 +338,39 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "codespan"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3362992a0d9f1dd7c3d0e89e0ab2bb540b7a95fea8cd798090e758fda2899b5e"
dependencies = [
"codespan-reporting",
"serde",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"serde",
"termcolor",
"unicode-width",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "core-foundation"
version = "0.9.1"
@ -398,6 +426,33 @@ dependencies = [
"subtle",
]
[[package]]
name = "cssparser"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a"
dependencies = [
"cssparser-macros",
"dtoa-short",
"itoa",
"matches",
"phf 0.8.0",
"proc-macro2",
"quote",
"smallvec",
"syn",
]
[[package]]
name = "cssparser-macros"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "ctor"
version = "0.1.20"
@ -419,6 +474,19 @@ dependencies = [
"syn",
]
[[package]]
name = "derive_more"
version = "0.99.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version",
"syn",
]
[[package]]
name = "digest"
version = "0.9.0"
@ -449,6 +517,27 @@ dependencies = [
"winapi",
]
[[package]]
name = "dtoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
[[package]]
name = "dtoa-short"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bde03329ae10e79ede66c9ce4dc930aa8599043b0743008548680f25b91502d6"
dependencies = [
"dtoa",
]
[[package]]
name = "dunce"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541"
[[package]]
name = "either"
version = "1.6.1"
@ -692,6 +781,15 @@ dependencies = [
"slab",
]
[[package]]
name = "fxhash"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
dependencies = [
"byteorder",
]
[[package]]
name = "generic-array"
version = "0.14.4"
@ -1080,6 +1178,18 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kuchiki"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358"
dependencies = [
"cssparser",
"html5ever",
"matches",
"selectors",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -1116,6 +1226,37 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "linkcheck"
version = "0.4.1-alpha.0"
source = "git+https://github.com/ankitects/linkcheck.git?rev=2f20798ce521cc594d510d4e417e76d5eac04d4b#2f20798ce521cc594d510d4e417e76d5eac04d4b"
dependencies = [
"bytes",
"codespan",
"dunce",
"futures",
"http",
"kuchiki",
"lazy_static",
"linkify",
"log",
"pulldown-cmark",
"regex",
"reqwest 0.11.4",
"serde",
"thiserror",
"url",
]
[[package]]
name = "linkify"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78d59d732ba6d7eeefc418aab8057dc8e3da4374bd5802ffa95bebc04b4d1dfb"
dependencies = [
"memchr",
]
[[package]]
name = "lock_api"
version = "0.4.4"
@ -1482,6 +1623,15 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pest"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
dependencies = [
"ucd-trie",
]
[[package]]
name = "petgraph"
version = "0.5.1"
@ -1498,7 +1648,9 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
dependencies = [
"phf_macros 0.8.0",
"phf_shared 0.8.0",
"proc-macro-hack",
]
[[package]]
@ -1507,7 +1659,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ac8b67553a7ca9457ce0e526948cad581819238f4a9d1ea74545851fa24f37"
dependencies = [
"phf_macros",
"phf_macros 0.9.0",
"phf_shared 0.9.0",
"proc-macro-hack",
]
@ -1542,6 +1694,20 @@ dependencies = [
"rand 0.8.4",
]
[[package]]
name = "phf_macros"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
dependencies = [
"phf_generator 0.8.0",
"phf_shared 0.8.0",
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "phf_macros"
version = "0.9.0"
@ -1973,6 +2139,40 @@ dependencies = [
"winreg",
]
[[package]]
name = "reqwest"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "246e9f61b9bb77df069a947682be06e31ac43ea37862e244a69f177694ea6d22"
dependencies = [
"base64",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"http",
"http-body",
"hyper",
"hyper-tls",
"ipnet",
"js-sys",
"lazy_static",
"log",
"mime",
"native-tls",
"percent-encoding",
"pin-project-lite",
"serde",
"serde_urlencoded",
"tokio",
"tokio-native-tls",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winreg",
]
[[package]]
name = "ring"
version = "0.16.20"
@ -2017,6 +2217,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_version"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
dependencies = [
"semver",
]
[[package]]
name = "rustls"
version = "0.19.1"
@ -2097,6 +2306,44 @@ dependencies = [
"libc",
]
[[package]]
name = "selectors"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe"
dependencies = [
"bitflags",
"cssparser",
"derive_more",
"fxhash",
"log",
"matches",
"phf 0.8.0",
"phf_codegen",
"precomputed-hash",
"servo_arc",
"smallvec",
"thin-slice",
]
[[package]]
name = "semver"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
dependencies = [
"pest",
]
[[package]]
name = "serde"
version = "1.0.126"
@ -2183,6 +2430,16 @@ dependencies = [
"serde",
]
[[package]]
name = "servo_arc"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432"
dependencies = [
"nodrop",
"stable_deref_trait",
]
[[package]]
name = "sha1"
version = "0.6.0"
@ -2422,6 +2679,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "thin-slice"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
[[package]]
name = "thiserror"
version = "1.0.25"
@ -2496,6 +2759,7 @@ dependencies = [
"mio",
"num_cpus",
"pin-project-lite",
"tokio-macros",
"winapi",
]
@ -2508,6 +2772,17 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-macros"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-native-tls"
version = "0.3.0"
@ -2611,6 +2886,12 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
[[package]]
name = "ucd-trie"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unic-char-property"
version = "0.9.0"
@ -2763,6 +3044,7 @@ dependencies = [
"idna",
"matches",
"percent-encoding",
"serde",
]
[[package]]

View file

@ -18,6 +18,8 @@ prost-build = "0.7.0"
[dev-dependencies]
env_logger = "0.8.4"
linkcheck = { git = "https://github.com/ankitects/linkcheck.git", rev = "2f20798ce521cc594d510d4e417e76d5eac04d4b" }
tokio = { version = "*", features = ["macros"] }
[dependencies]
# pinned as any changes could invalidate sqlite indexes

View file

@ -18,6 +18,7 @@ pub mod error;
pub mod findreplace;
pub mod i18n;
pub mod latex;
pub mod links;
pub mod log;
mod markdown;
pub mod media;

122
rslib/src/links.rs Normal file
View file

@ -0,0 +1,122 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
use strum::{Display, EnumIter, EnumString};
static HELP_SITE: &'static str = "https://docs.ankiweb.net/";
#[derive(Debug, PartialEq, Clone, Copy, Display, EnumIter, EnumString)]
pub enum HelpPage {
#[strum(serialize = "getting-started#note-types")]
Notetype,
#[strum(serialize = "browsing")]
Browsing,
#[strum(serialize = "browsing#find-and-replace")]
BrowsingFindAndReplace,
#[strum(serialize = "browsing#notes")]
BrowsingNotesMenu,
#[strum(serialize = "studying#keyboard-shortcuts")]
KeyboardShortcuts,
#[strum(serialize = "editing")]
Editing,
#[strum(serialize = "editing#adding-cards-and-notes")]
AddingCardAndNote,
#[strum(serialize = "editing#adding-a-note-type")]
AddingNotetype,
#[strum(serialize = "math#latex")]
Latex,
#[strum(serialize = "preferences")]
Preferences,
#[strum(serialize = "")]
Index,
#[strum(serialize = "templates/intro")]
Templates,
#[strum(serialize = "filtered-decks")]
FilteredDeck,
#[strum(serialize = "importing")]
Importing,
#[strum(serialize = "editing#customizing-fields")]
CustomizingFields,
#[strum(serialize = "deck-options")]
DeckOptions,
#[strum(serialize = "editing#features")]
EditingFeatures,
}
pub fn help_page_link(page: HelpPage) -> String {
format!("{}{}", HELP_SITE, page)
}
pub fn help_page_link_from_str(page: &str) -> String {
format!("{}{}", HELP_SITE, page)
}
#[cfg(test)]
mod test {
use super::*;
use futures::StreamExt;
use itertools::Itertools;
use linkcheck::{
validation::{check_web, Context, Reason},
BasicContext,
};
use reqwest::Url;
use std::iter;
use strum::IntoEnumIterator;
/// Aggregates [`Outcome`]s by collecting the error messages of the invalid ones.
#[derive(Default)]
struct Outcomes(Vec<String>);
enum Outcome {
Valid,
Invalid(String),
}
#[tokio::test]
async fn check_links() {
let ctx = BasicContext::default();
let result = futures::stream::iter(HelpPage::iter())
.map(|page| check_page(page, &ctx))
.buffer_unordered(ctx.concurrency())
.collect::<Outcomes>()
.await;
if result.0.len() > 0 {
panic!(result.message());
}
}
async fn check_page(page: HelpPage, ctx: &BasicContext) -> Outcome {
match Url::parse(&help_page_link(page)) {
Ok(url) => match check_web(&url, ctx).await {
Ok(()) => Outcome::Valid,
Err(Reason::Dom) => {
Outcome::Invalid(format!("'{}' not found on '{}'", page, HELP_SITE))
}
Err(Reason::Web(err)) => Outcome::Invalid(err.to_string()),
_ => unreachable!(),
},
Err(err) => Outcome::Invalid(err.to_string()),
}
}
impl Extend<Outcome> for Outcomes {
fn extend<T: IntoIterator<Item = Outcome>>(&mut self, items: T) {
for outcome in items {
match outcome {
Outcome::Valid => (),
Outcome::Invalid(err) => self.0.push(err),
}
}
}
}
impl Outcomes {
fn message(&self) -> String {
iter::once(&format!("{} links could not be validated:", self.0.len()))
.chain(self.0.iter())
.join("\n - ")
}
}
}