2017-07-01 07:47:41 +00:00
|
|
|
use std::collections::HashMap;
|
Fix clippy warnings (#744)
Clippy is returning some warnings. Let's fix or explicitly ignore
them. In particular:
- In `components/imageproc/src/lib.rs`, we implement `Hash` explicitly
but derive `PartialEq`. We need to maintain the property that two
keys being equal implies the hashes of those two keys are equal.
Our `Hash` implementations preserve this, so we'll explicitly ignore
the warnings.
- In `components/site/src/lib.rs`, we were calling `.into()` on some
values that are already of the correct type.
- In `components/site/src/lib.rs`, we were using `.map(|x| *x)` in
iterator chains to remove a level of indirection; we can instead say
`.copied()` (introduced in Rust v1.36) or `.cloned()`. Using
`.copied` here is better from a type-checking point of view, but
we'll use `.cloned` for now as Rust v1.36 was only recently
released.
- In `components/templates/src/filters.rs` and
`components/utils/src/site.rs`, we were taking `HashMap`s as
function arguments but not generically accepting alternate `Hasher`
implementations.
- In `src/cmd/check.rs`, we use `env::current_dir()` as a default
value, but our use of `unwrap_or` meant that we would always
retrieve the current directory even when not needed.
- In `components/errors/src/lib.rs`, we can use `if let` rather than
`match`.
- In `components/library/src/content/page.rs`, we can collapse a
nested conditional into `else if let ...`.
- In `components/library/src/sorting.rs`, a function takes `&&Page`
arguments. Clippy warns about this for efficiency reasons, but
we're doing it here to match a particular sorting API, so we'll
explicitly ignore the warning.
2019-07-12 08:29:44 +00:00
|
|
|
use std::hash::BuildHasher;
|
2018-05-16 21:52:26 +00:00
|
|
|
use unicode_segmentation::UnicodeSegmentation;
|
2017-07-01 07:47:41 +00:00
|
|
|
|
2019-12-21 21:52:39 +00:00
|
|
|
use errors::{bail, Result};
|
2017-07-01 07:47:41 +00:00
|
|
|
|
|
|
|
/// Get word count and estimated reading time
|
|
|
|
pub fn get_reading_analytics(content: &str) -> (usize, usize) {
|
2018-05-16 21:52:26 +00:00
|
|
|
let word_count: usize = content.unicode_words().count();
|
2017-07-01 07:47:41 +00:00
|
|
|
|
|
|
|
// https://help.medium.com/hc/en-us/articles/214991667-Read-time
|
|
|
|
// 275 seems a bit too high though
|
2019-06-16 06:26:16 +00:00
|
|
|
(word_count, ((word_count + 199) / 200))
|
2017-07-01 07:47:41 +00:00
|
|
|
}
|
|
|
|
|
2019-06-06 17:49:31 +00:00
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
|
|
pub struct ResolvedInternalLink {
|
|
|
|
pub permalink: String,
|
|
|
|
// The 2 fields below are only set when there is an anchor
|
|
|
|
// as we will need that to check if it exists after the markdown rendering is done
|
|
|
|
pub md_path: Option<String>,
|
|
|
|
pub anchor: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Resolves an internal link (of the `@/posts/something.md#hey` sort) to its absolute link and
|
|
|
|
/// returns the path + anchor as well
|
Fix clippy warnings (#744)
Clippy is returning some warnings. Let's fix or explicitly ignore
them. In particular:
- In `components/imageproc/src/lib.rs`, we implement `Hash` explicitly
but derive `PartialEq`. We need to maintain the property that two
keys being equal implies the hashes of those two keys are equal.
Our `Hash` implementations preserve this, so we'll explicitly ignore
the warnings.
- In `components/site/src/lib.rs`, we were calling `.into()` on some
values that are already of the correct type.
- In `components/site/src/lib.rs`, we were using `.map(|x| *x)` in
iterator chains to remove a level of indirection; we can instead say
`.copied()` (introduced in Rust v1.36) or `.cloned()`. Using
`.copied` here is better from a type-checking point of view, but
we'll use `.cloned` for now as Rust v1.36 was only recently
released.
- In `components/templates/src/filters.rs` and
`components/utils/src/site.rs`, we were taking `HashMap`s as
function arguments but not generically accepting alternate `Hasher`
implementations.
- In `src/cmd/check.rs`, we use `env::current_dir()` as a default
value, but our use of `unwrap_or` meant that we would always
retrieve the current directory even when not needed.
- In `components/errors/src/lib.rs`, we can use `if let` rather than
`match`.
- In `components/library/src/content/page.rs`, we can collapse a
nested conditional into `else if let ...`.
- In `components/library/src/sorting.rs`, a function takes `&&Page`
arguments. Clippy warns about this for efficiency reasons, but
we're doing it here to match a particular sorting API, so we'll
explicitly ignore the warning.
2019-07-12 08:29:44 +00:00
|
|
|
pub fn resolve_internal_link<S: BuildHasher>(
|
2019-06-06 17:49:31 +00:00
|
|
|
link: &str,
|
Fix clippy warnings (#744)
Clippy is returning some warnings. Let's fix or explicitly ignore
them. In particular:
- In `components/imageproc/src/lib.rs`, we implement `Hash` explicitly
but derive `PartialEq`. We need to maintain the property that two
keys being equal implies the hashes of those two keys are equal.
Our `Hash` implementations preserve this, so we'll explicitly ignore
the warnings.
- In `components/site/src/lib.rs`, we were calling `.into()` on some
values that are already of the correct type.
- In `components/site/src/lib.rs`, we were using `.map(|x| *x)` in
iterator chains to remove a level of indirection; we can instead say
`.copied()` (introduced in Rust v1.36) or `.cloned()`. Using
`.copied` here is better from a type-checking point of view, but
we'll use `.cloned` for now as Rust v1.36 was only recently
released.
- In `components/templates/src/filters.rs` and
`components/utils/src/site.rs`, we were taking `HashMap`s as
function arguments but not generically accepting alternate `Hasher`
implementations.
- In `src/cmd/check.rs`, we use `env::current_dir()` as a default
value, but our use of `unwrap_or` meant that we would always
retrieve the current directory even when not needed.
- In `components/errors/src/lib.rs`, we can use `if let` rather than
`match`.
- In `components/library/src/content/page.rs`, we can collapse a
nested conditional into `else if let ...`.
- In `components/library/src/sorting.rs`, a function takes `&&Page`
arguments. Clippy warns about this for efficiency reasons, but
we're doing it here to match a particular sorting API, so we'll
explicitly ignore the warning.
2019-07-12 08:29:44 +00:00
|
|
|
permalinks: &HashMap<String, String, S>,
|
2019-06-06 17:49:31 +00:00
|
|
|
) -> Result<ResolvedInternalLink> {
|
2018-10-18 20:50:06 +00:00
|
|
|
// First we remove the ./ since that's zola specific
|
2019-05-27 12:35:14 +00:00
|
|
|
let clean_link = link.replacen("@/", "", 1);
|
2017-07-01 07:47:41 +00:00
|
|
|
// Then we remove any potential anchor
|
|
|
|
// parts[0] will be the file path and parts[1] the anchor if present
|
|
|
|
let parts = clean_link.split('#').collect::<Vec<_>>();
|
|
|
|
match permalinks.get(parts[0]) {
|
|
|
|
Some(p) => {
|
|
|
|
if parts.len() > 1 {
|
2019-06-06 17:49:31 +00:00
|
|
|
Ok(ResolvedInternalLink {
|
|
|
|
permalink: format!("{}#{}", p, parts[1]),
|
|
|
|
md_path: Some(parts[0].to_string()),
|
|
|
|
anchor: Some(parts[1].to_string()),
|
|
|
|
})
|
2017-07-01 07:47:41 +00:00
|
|
|
} else {
|
2019-06-06 17:49:31 +00:00
|
|
|
Ok(ResolvedInternalLink { permalink: p.to_string(), md_path: None, anchor: None })
|
2017-07-01 07:47:41 +00:00
|
|
|
}
|
2018-07-31 13:17:31 +00:00
|
|
|
}
|
2017-07-01 07:47:41 +00:00
|
|
|
None => bail!(format!("Relative link {} not found.", link)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2018-10-31 07:18:57 +00:00
|
|
|
use super::{get_reading_analytics, resolve_internal_link};
|
2017-07-01 07:47:41 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn can_resolve_valid_internal_link() {
|
|
|
|
let mut permalinks = HashMap::new();
|
|
|
|
permalinks.insert("pages/about.md".to_string(), "https://vincent.is/about".to_string());
|
2019-05-27 12:35:14 +00:00
|
|
|
let res = resolve_internal_link("@/pages/about.md", &permalinks).unwrap();
|
2019-06-06 17:49:31 +00:00
|
|
|
assert_eq!(res.permalink, "https://vincent.is/about");
|
2017-07-01 07:47:41 +00:00
|
|
|
}
|
|
|
|
|
2018-03-29 12:55:25 +00:00
|
|
|
#[test]
|
|
|
|
fn can_resolve_valid_root_internal_link() {
|
|
|
|
let mut permalinks = HashMap::new();
|
|
|
|
permalinks.insert("about.md".to_string(), "https://vincent.is/about".to_string());
|
2019-05-27 12:35:14 +00:00
|
|
|
let res = resolve_internal_link("@/about.md", &permalinks).unwrap();
|
2019-06-06 17:49:31 +00:00
|
|
|
assert_eq!(res.permalink, "https://vincent.is/about");
|
2018-03-29 12:55:25 +00:00
|
|
|
}
|
|
|
|
|
2017-07-01 07:47:41 +00:00
|
|
|
#[test]
|
|
|
|
fn can_resolve_internal_links_with_anchors() {
|
|
|
|
let mut permalinks = HashMap::new();
|
|
|
|
permalinks.insert("pages/about.md".to_string(), "https://vincent.is/about".to_string());
|
2019-05-27 12:35:14 +00:00
|
|
|
let res = resolve_internal_link("@/pages/about.md#hello", &permalinks).unwrap();
|
2019-06-06 17:49:31 +00:00
|
|
|
assert_eq!(res.permalink, "https://vincent.is/about#hello");
|
|
|
|
assert_eq!(res.md_path, Some("pages/about.md".to_string()));
|
|
|
|
assert_eq!(res.anchor, Some("hello".to_string()));
|
2017-07-01 07:47:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn errors_resolve_inexistant_internal_link() {
|
2019-05-27 12:35:14 +00:00
|
|
|
let res = resolve_internal_link("@/pages/about.md#hello", &HashMap::new());
|
2017-07-01 07:47:41 +00:00
|
|
|
assert!(res.is_err());
|
|
|
|
}
|
|
|
|
|
2019-06-16 06:26:16 +00:00
|
|
|
#[test]
|
|
|
|
fn reading_analytics_empty_text() {
|
|
|
|
let (word_count, reading_time) = get_reading_analytics(" ");
|
|
|
|
assert_eq!(word_count, 0);
|
|
|
|
assert_eq!(reading_time, 0);
|
|
|
|
}
|
|
|
|
|
2017-07-01 07:47:41 +00:00
|
|
|
#[test]
|
|
|
|
fn reading_analytics_short_text() {
|
|
|
|
let (word_count, reading_time) = get_reading_analytics("Hello World");
|
|
|
|
assert_eq!(word_count, 2);
|
2019-06-16 06:26:16 +00:00
|
|
|
assert_eq!(reading_time, 1);
|
2017-07-01 07:47:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reading_analytics_long_text() {
|
|
|
|
let mut content = String::new();
|
|
|
|
for _ in 0..1000 {
|
|
|
|
content.push_str(" Hello world");
|
|
|
|
}
|
|
|
|
let (word_count, reading_time) = get_reading_analytics(&content);
|
|
|
|
assert_eq!(word_count, 2000);
|
|
|
|
assert_eq!(reading_time, 10);
|
|
|
|
}
|
|
|
|
}
|