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.

90 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 => {
  34. s.replace(|c: char| c.is_ascii_whitespace(), "_")
  35. }
  36. }
  37. }
  38. #[cfg(test)]
  39. mod tests {
  40. use super::*;
  41. #[test]
  42. fn can_slugify_paths() {
  43. let tests = vec![
  44. // input, (on, safe, off)
  45. ("input", ("input", "input", "input")),
  46. ("test ", ("test", "test", "test ")),
  47. ("tes t", ("tes-t", "tes t", "tes t")),
  48. // Invalid NTFS
  49. ("dot. ", ("dot", "dot", "dot. ")),
  50. ("日本", ("ri-ben", "日本", "日本")),
  51. ("héhé", ("hehe", "héhé", "héhé")),
  52. ("test (hey)", ("test-hey", "test (hey)", "test (hey)")),
  53. ];
  54. for (input, (on, safe, off)) in tests {
  55. assert_eq!(on, slugify_paths(input, SlugifyStrategy::On));
  56. assert_eq!(safe, slugify_paths(input, SlugifyStrategy::Safe));
  57. assert_eq!(off, slugify_paths(input, SlugifyStrategy::Off));
  58. }
  59. }
  60. #[test]
  61. fn can_slugify_anchors() {
  62. let tests = vec![
  63. // input, (on, safe, off)
  64. ("input", ("input", "input", "input")),
  65. ("test ", ("test", "test_", "test_")),
  66. ("tes t", ("tes-t", "tes_t", "tes_t")),
  67. // Invalid NTFS
  68. ("dot. ", ("dot", "dot._", "dot._")),
  69. ("日本", ("ri-ben", "日本", "日本")),
  70. ("héhé", ("hehe", "héhé", "héhé")),
  71. ("test (hey)", ("test-hey", "test_(hey)", "test_(hey)")),
  72. ];
  73. for (input, (on, safe, off)) in tests {
  74. assert_eq!(on, slugify_anchors(input, SlugifyStrategy::On));
  75. assert_eq!(safe, slugify_anchors(input, SlugifyStrategy::Safe));
  76. assert_eq!(off, slugify_anchors(input, SlugifyStrategy::Off));
  77. }
  78. }
  79. }