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.

228 lines
7.6KB

  1. use std::fs::{canonicalize, create_dir};
  2. use std::path::Path;
  3. use errors::{bail, Result};
  4. use utils::fs::create_file;
  5. use crate::console;
  6. use crate::prompt::{ask_bool, ask_url};
  7. const CONFIG: &str = r#"
  8. # The URL the site will be built for
  9. base_url = "%BASE_URL%"
  10. # Whether to automatically compile all Sass files in the sass directory
  11. compile_sass = %COMPILE_SASS%
  12. # Whether to do syntax highlighting
  13. # Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
  14. highlight_code = %HIGHLIGHT%
  15. # Whether to build a search index to be used later on by a JavaScript library
  16. build_search_index = %SEARCH%
  17. [extra]
  18. # Put all your custom variables here
  19. "#;
  20. // Given a path, return true if it is a directory and it doesn't have any
  21. // non-hidden files, otherwise return false (path is assumed to exist)
  22. pub fn is_directory_quasi_empty(path: &Path) -> Result<bool> {
  23. if path.is_dir() {
  24. let mut entries = match path.read_dir() {
  25. Ok(entries) => entries,
  26. Err(e) => {
  27. bail!(
  28. "Could not read `{}` because of error: {}",
  29. path.to_string_lossy().to_string(),
  30. e
  31. );
  32. }
  33. };
  34. // If any entry raises an error or isn't hidden (i.e. starts with `.`), we raise an error
  35. if entries.any(|x| match x {
  36. Ok(file) => !file
  37. .file_name()
  38. .to_str()
  39. .expect("Could not convert filename to &str")
  40. .starts_with('.'),
  41. Err(_) => true,
  42. }) {
  43. return Ok(false);
  44. }
  45. return Ok(true);
  46. }
  47. Ok(false)
  48. }
  49. pub fn create_new_project(name: &str) -> Result<()> {
  50. let path = Path::new(name);
  51. // Better error message than the rust default
  52. if path.exists() && !is_directory_quasi_empty(&path)? {
  53. if name == "." {
  54. bail!("The current directory is not an empty folder (hidden files are ignored).");
  55. } else {
  56. bail!(
  57. "`{}` is not an empty folder (hidden files are ignored).",
  58. path.to_string_lossy().to_string()
  59. )
  60. }
  61. }
  62. console::info("Welcome to Zola!");
  63. console::info("Please answer a few questions to get started quickly.");
  64. console::info("Any choices made can be changed by modifying the `config.toml` file later.");
  65. let base_url = ask_url("> What is the URL of your site?", "https://example.com")?;
  66. let compile_sass = ask_bool("> Do you want to enable Sass compilation?", true)?;
  67. let highlight = ask_bool("> Do you want to enable syntax highlighting?", false)?;
  68. let search = ask_bool("> Do you want to build a search index of the content?", false)?;
  69. let config = CONFIG
  70. .trim_start()
  71. .replace("%BASE_URL%", &base_url)
  72. .replace("%COMPILE_SASS%", &format!("{}", compile_sass))
  73. .replace("%SEARCH%", &format!("{}", search))
  74. .replace("%HIGHLIGHT%", &format!("{}", highlight));
  75. populate(&path, compile_sass, &config)?;
  76. println!();
  77. console::success(&format!("Done! Your site was created in {:?}", canonicalize(path).unwrap()));
  78. println!();
  79. console::info(
  80. "Get started by moving into the directory and using the built-in server: `zola serve`",
  81. );
  82. println!("Visit https://www.getzola.org for the full documentation.");
  83. Ok(())
  84. }
  85. fn populate(path: &Path, compile_sass: bool, config: &str) -> Result<()> {
  86. if !path.exists() {
  87. create_dir(path)?;
  88. }
  89. create_file(&path.join("config.toml"), &config)?;
  90. create_dir(path.join("content"))?;
  91. create_dir(path.join("templates"))?;
  92. create_dir(path.join("static"))?;
  93. create_dir(path.join("themes"))?;
  94. if compile_sass {
  95. create_dir(path.join("sass"))?;
  96. }
  97. Ok(())
  98. }
  99. #[cfg(test)]
  100. mod tests {
  101. use super::*;
  102. use std::env::temp_dir;
  103. use std::fs::{create_dir, remove_dir, remove_dir_all};
  104. #[test]
  105. fn init_empty_directory() {
  106. let mut dir = temp_dir();
  107. dir.push("test_empty_dir");
  108. if dir.exists() {
  109. remove_dir_all(&dir).expect("Could not free test directory");
  110. }
  111. create_dir(&dir).expect("Could not create test directory");
  112. let allowed = is_directory_quasi_empty(&dir)
  113. .expect("An error happened reading the directory's contents");
  114. remove_dir(&dir).unwrap();
  115. assert_eq!(true, allowed);
  116. }
  117. #[test]
  118. fn init_non_empty_directory() {
  119. let mut dir = temp_dir();
  120. dir.push("test_non_empty_dir");
  121. if dir.exists() {
  122. remove_dir_all(&dir).expect("Could not free test directory");
  123. }
  124. create_dir(&dir).expect("Could not create test directory");
  125. let mut content = dir.clone();
  126. content.push("content");
  127. create_dir(&content).unwrap();
  128. let allowed = is_directory_quasi_empty(&dir)
  129. .expect("An error happened reading the directory's contents");
  130. remove_dir(&content).unwrap();
  131. remove_dir(&dir).unwrap();
  132. assert_eq!(false, allowed);
  133. }
  134. #[test]
  135. fn init_quasi_empty_directory() {
  136. let mut dir = temp_dir();
  137. dir.push("test_quasi_empty_dir");
  138. if dir.exists() {
  139. remove_dir_all(&dir).expect("Could not free test directory");
  140. }
  141. create_dir(&dir).expect("Could not create test directory");
  142. let mut git = dir.clone();
  143. git.push(".git");
  144. create_dir(&git).unwrap();
  145. let allowed = is_directory_quasi_empty(&dir)
  146. .expect("An error happened reading the directory's contents");
  147. remove_dir(&git).unwrap();
  148. remove_dir(&dir).unwrap();
  149. assert_eq!(true, allowed);
  150. }
  151. #[test]
  152. fn populate_existing_directory() {
  153. let mut dir = temp_dir();
  154. dir.push("test_existing_dir");
  155. if dir.exists() {
  156. remove_dir_all(&dir).expect("Could not free test directory");
  157. }
  158. create_dir(&dir).expect("Could not create test directory");
  159. populate(&dir, true, "").expect("Could not populate zola directories");
  160. assert_eq!(true, dir.join("config.toml").exists());
  161. assert_eq!(true, dir.join("content").exists());
  162. assert_eq!(true, dir.join("templates").exists());
  163. assert_eq!(true, dir.join("static").exists());
  164. assert_eq!(true, dir.join("themes").exists());
  165. assert_eq!(true, dir.join("sass").exists());
  166. remove_dir_all(&dir).unwrap();
  167. }
  168. #[test]
  169. fn populate_non_existing_directory() {
  170. let mut dir = temp_dir();
  171. dir.push("test_non_existing_dir");
  172. if dir.exists() {
  173. remove_dir_all(&dir).expect("Could not free test directory");
  174. }
  175. populate(&dir, true, "").expect("Could not populate zola directories");
  176. assert_eq!(true, dir.exists());
  177. assert_eq!(true, dir.join("config.toml").exists());
  178. assert_eq!(true, dir.join("content").exists());
  179. assert_eq!(true, dir.join("templates").exists());
  180. assert_eq!(true, dir.join("static").exists());
  181. assert_eq!(true, dir.join("themes").exists());
  182. assert_eq!(true, dir.join("sass").exists());
  183. remove_dir_all(&dir).unwrap();
  184. }
  185. #[test]
  186. fn populate_without_sass() {
  187. let mut dir = temp_dir();
  188. dir.push("test_wihout_sass_dir");
  189. if dir.exists() {
  190. remove_dir_all(&dir).expect("Could not free test directory");
  191. }
  192. create_dir(&dir).expect("Could not create test directory");
  193. populate(&dir, false, "").expect("Could not populate zola directories");
  194. assert_eq!(false, dir.join("sass").exists());
  195. remove_dir_all(&dir).unwrap();
  196. }
  197. }