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.

121 lines
3.7KB

  1. use std::path::{Path, PathBuf};
  2. /// Takes a full path to a file and returns only the components after the first `content` directory
  3. /// Will not return the filename as last component
  4. pub fn find_content_components<P: AsRef<Path>>(path: P) -> Vec<String> {
  5. let path = path.as_ref();
  6. let mut is_in_content = false;
  7. let mut components = vec![];
  8. for section in path.parent().unwrap().components() {
  9. let component = section.as_os_str().to_string_lossy();
  10. if is_in_content {
  11. components.push(component.to_string());
  12. continue;
  13. }
  14. if component == "content" {
  15. is_in_content = true;
  16. }
  17. }
  18. components
  19. }
  20. /// Struct that contains all the information about the actual file
  21. #[derive(Debug, Clone, PartialEq)]
  22. pub struct FileInfo {
  23. /// The full path to the .md file
  24. pub path: PathBuf,
  25. /// The name of the .md file without the extension, always `_index` for sections
  26. pub name: String,
  27. /// The .md path, starting from the content directory, with `/` slashes
  28. pub relative: String,
  29. /// Path of the directory containing the .md file
  30. pub parent: PathBuf,
  31. /// Path of the grand parent directory for that file. Only used in sections to find subsections.
  32. pub grand_parent: Option<PathBuf>,
  33. /// The folder names to this section file, starting from the `content` directory
  34. /// For example a file at content/kb/solutions/blabla.md will have 2 components:
  35. /// `kb` and `solutions`
  36. pub components: Vec<String>,
  37. }
  38. impl FileInfo {
  39. pub fn new_page(path: &Path) -> FileInfo {
  40. let file_path = path.to_path_buf();
  41. let mut parent = file_path.parent().unwrap().to_path_buf();
  42. let name = path.file_stem().unwrap().to_string_lossy().to_string();
  43. let mut components = find_content_components(&file_path);
  44. let relative = if !components.is_empty() {
  45. format!("{}/{}.md", components.join("/"), name)
  46. } else {
  47. format!("{}.md", name)
  48. };
  49. // If we have a folder with an asset, don't consider it as a component
  50. if !components.is_empty() && name == "index" {
  51. components.pop();
  52. // also set parent_path to grandparent instead
  53. parent = parent.parent().unwrap().to_path_buf();
  54. }
  55. FileInfo {
  56. path: file_path,
  57. // We don't care about grand parent for pages
  58. grand_parent: None,
  59. parent,
  60. name,
  61. components,
  62. relative,
  63. }
  64. }
  65. pub fn new_section(path: &Path) -> FileInfo {
  66. let parent = path.parent().unwrap().to_path_buf();
  67. let components = find_content_components(path);
  68. let relative = if components.is_empty() {
  69. // the index one
  70. "_index.md".to_string()
  71. } else {
  72. format!("{}/_index.md", components.join("/"))
  73. };
  74. let grand_parent = parent.parent().map(|p| p.to_path_buf());
  75. FileInfo {
  76. path: path.to_path_buf(),
  77. parent,
  78. grand_parent,
  79. name: "_index".to_string(),
  80. components,
  81. relative,
  82. }
  83. }
  84. }
  85. #[doc(hidden)]
  86. impl Default for FileInfo {
  87. fn default() -> FileInfo {
  88. FileInfo {
  89. path: PathBuf::new(),
  90. parent: PathBuf::new(),
  91. grand_parent: None,
  92. name: String::new(),
  93. components: vec![],
  94. relative: String::new(),
  95. }
  96. }
  97. }
  98. #[cfg(test)]
  99. mod tests {
  100. use super::find_content_components;
  101. #[test]
  102. fn can_find_content_components() {
  103. let res = find_content_components("/home/vincent/code/site/content/posts/tutorials/python.md");
  104. assert_eq!(res, ["posts".to_string(), "tutorials".to_string()]);
  105. }
  106. }