You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

88 lines
3.0KB

  1. use serde_derive::{Deserialize, Serialize};
  2. #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
  3. #[serde(rename_all = "lowercase")]
  4. pub enum SlugifyStrategy {
  5. /// Classic slugification, the default
  6. On,
  7. /// No slugification, only remove unsafe characters for filepaths/urls
  8. Safe,
  9. /// Nothing is changed, hope for the best!
  10. Off,
  11. }
  12. fn strip_chars(s: &str, chars: &str) -> String {
  13. let mut sanitized_string = s.to_string();
  14. sanitized_string.retain(|c| !chars.contains(c));
  15. sanitized_string
  16. }
  17. fn strip_invalid_paths_chars(s: &str) -> String {
  18. // NTFS forbidden characters : https://gist.github.com/doctaphred/d01d05291546186941e1b7ddc02034d3
  19. // Also we need to trim whitespaces and `.` from the end of filename
  20. let trimmed = s.trim_end_matches(|c| c == ' ' || c == '.');
  21. strip_chars(&trimmed, r#"<>:"/\|?*"#)
  22. }
  23. pub fn slugify_paths(s: &str, strategy: SlugifyStrategy) -> String {
  24. match strategy {
  25. SlugifyStrategy::On => slug::slugify(s),
  26. SlugifyStrategy::Safe => strip_invalid_paths_chars(s),
  27. SlugifyStrategy::Off => s.to_string(),
  28. }
  29. }
  30. pub fn slugify_anchors(s: &str, strategy: SlugifyStrategy) -> String {
  31. match strategy {
  32. SlugifyStrategy::On => slug::slugify(s),
  33. SlugifyStrategy::Safe | SlugifyStrategy::Off => s.replace(|c: char| c.is_ascii_whitespace(), "_"),
  34. }
  35. }
  36. #[cfg(test)]
  37. mod tests {
  38. use super::*;
  39. #[test]
  40. fn can_slugify_paths() {
  41. let tests = vec![
  42. // input, (on, safe, off)
  43. ("input", ("input", "input", "input")),
  44. ("test ", ("test", "test", "test ")),
  45. ("tes t", ("tes-t", "tes t", "tes t")),
  46. // Invalid NTFS
  47. ("dot. ", ("dot", "dot", "dot. ")),
  48. ("日本", ("ri-ben", "日本", "日本")),
  49. ("héhé", ("hehe", "héhé", "héhé")),
  50. ("test (hey)", ("test-hey", "test (hey)", "test (hey)")),
  51. ];
  52. for (input, (on, safe, off)) in tests {
  53. assert_eq!(on, slugify_paths(input, SlugifyStrategy::On));
  54. assert_eq!(safe, slugify_paths(input, SlugifyStrategy::Safe));
  55. assert_eq!(off, slugify_paths(input, SlugifyStrategy::Off));
  56. }
  57. }
  58. #[test]
  59. fn can_slugify_anchors() {
  60. let tests = vec![
  61. // input, (on, safe, off)
  62. ("input", ("input", "input", "input")),
  63. ("test ", ("test", "test_", "test_")),
  64. ("tes t", ("tes-t", "tes_t", "tes_t")),
  65. // Invalid NTFS
  66. ("dot. ", ("dot", "dot._", "dot._")),
  67. ("日本", ("ri-ben", "日本", "日本")),
  68. ("héhé", ("hehe", "héhé", "héhé")),
  69. ("test (hey)", ("test-hey", "test_(hey)", "test_(hey)")),
  70. ];
  71. for (input, (on, safe, off)) in tests {
  72. assert_eq!(on, slugify_anchors(input, SlugifyStrategy::On));
  73. assert_eq!(safe, slugify_anchors(input, SlugifyStrategy::Safe));
  74. assert_eq!(off, slugify_anchors(input, SlugifyStrategy::Off));
  75. }
  76. }
  77. }