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.

150 lines
4.8KB

  1. use clap::ArgMatches;
  2. use std::convert::From;
  3. use std::path::PathBuf;
  4. use tantivy;
  5. use tantivy::schema::*;
  6. use tantivy::Index;
  7. use std::io;
  8. use ansi_term::Style;
  9. use ansi_term::Colour::{Red, Blue, Green};
  10. use std::io::Write;
  11. use std::ascii::AsciiExt;
  12. use rustc_serialize::json;
  13. pub fn run_new_cli(matches: &ArgMatches) -> Result<(), String> {
  14. let index_directory = PathBuf::from(matches.value_of("index").unwrap());
  15. run_new(index_directory).map_err(|e| format!("{:?}" , e))
  16. }
  17. fn prompt_input<P: Fn(&str) -> Result<(), String>>(prompt_text: &str, predicate: P) -> String {
  18. loop {
  19. print!("{prompt_text:<width$} ? ", prompt_text=Style::new().bold().fg(Blue).paint(prompt_text), width=40);
  20. io::stdout().flush().unwrap();
  21. let mut buffer = String::new();
  22. io::stdin().read_line(&mut buffer).ok().expect("Failed to read line");
  23. let answer = buffer.trim_right_matches("\n").to_string();
  24. match predicate(&answer) {
  25. Ok(()) => {
  26. return answer;
  27. }
  28. Err(msg) => {
  29. println!("Error: {}", Style::new().bold().fg(Red).paint(msg));
  30. }
  31. }
  32. }
  33. }
  34. fn field_name_validate(field_name: &str) -> Result<(), String> {
  35. if is_valid_field_name(field_name) {
  36. Ok(())
  37. }
  38. else {
  39. Err(String::from("Field name must match the pattern [_a-zA-Z0-9]+"))
  40. }
  41. }
  42. fn prompt_options(msg: &str, codes: Vec<char>) -> char {
  43. let options_string: Vec<String> = codes.iter().map(|c| format!("{}", c)).collect();
  44. let options = options_string.join("/");
  45. let predicate = |entry: &str| {
  46. if entry.len() != 1 {
  47. return Err(format!("Invalid input. Options are ({})", options))
  48. }
  49. let c = entry.chars().next().unwrap().to_ascii_uppercase();
  50. if codes.contains(&c) {
  51. return Ok(())
  52. }
  53. else {
  54. return Err(format!("Invalid input. Options are ({})", options))
  55. }
  56. };
  57. let message = format!("{} ({})", msg, options);
  58. let entry = prompt_input(&message, predicate);
  59. entry.chars().next().unwrap().to_ascii_uppercase()
  60. }
  61. fn prompt_yn(msg: &str) -> bool {
  62. prompt_options(msg, vec!('Y', 'N')) == 'Y'
  63. }
  64. fn ask_add_field_text(field_name: &str, schema_builder: &mut SchemaBuilder) {
  65. let mut text_options = TextOptions::new();
  66. if prompt_yn("Should the field be stored") {
  67. text_options = text_options.set_stored();
  68. }
  69. let is_indexed = prompt_yn("Should the field be indexed");
  70. let indexing_options = if is_indexed {
  71. if prompt_yn("Should the field be tokenized") {
  72. if prompt_yn("Should the term frequencies (per doc) be in the index") {
  73. if prompt_yn("Should the term positions (per doc) be in the index") {
  74. TextIndexingOptions::TokenizedWithFreqAndPosition
  75. }
  76. else {
  77. TextIndexingOptions::TokenizedWithFreq
  78. }
  79. }
  80. else {
  81. TextIndexingOptions::TokenizedNoFreq
  82. }
  83. }
  84. else {
  85. TextIndexingOptions::Unindexed
  86. }
  87. }
  88. else {
  89. TextIndexingOptions::Unindexed
  90. };
  91. text_options = text_options.set_indexing_options(indexing_options);
  92. schema_builder.add_text_field(field_name, text_options);
  93. }
  94. fn ask_add_field_u32(field_name: &str, schema_builder: &mut SchemaBuilder) {
  95. let mut u32_options = U32Options::new();
  96. if prompt_yn("Should the field be stored") {
  97. u32_options = u32_options.set_stored();
  98. }
  99. if prompt_yn("Should the field be fast") {
  100. u32_options = u32_options.set_fast();
  101. }
  102. if prompt_yn("Should the field be indexed") {
  103. u32_options = u32_options.set_indexed();
  104. }
  105. schema_builder.add_u32_field(field_name, u32_options);
  106. }
  107. fn ask_add_field(schema_builder: &mut SchemaBuilder) {
  108. println!("\n\n");
  109. let field_name = prompt_input("New field name ", field_name_validate);
  110. let text_or_integer = prompt_options("Text or unsigned 32-bit Integer", vec!('T', 'I'));
  111. if text_or_integer =='T' {
  112. ask_add_field_text(&field_name, schema_builder);
  113. }
  114. else {
  115. ask_add_field_u32(&field_name, schema_builder);
  116. }
  117. }
  118. fn run_new(directory: PathBuf) -> tantivy::Result<()> {
  119. println!("\n{} ", Style::new().bold().fg(Green).paint("Creating new index"));
  120. println!("{} ", Style::new().bold().fg(Green).paint("Let's define it's schema!"));
  121. let mut schema_builder = SchemaBuilder::new();
  122. loop {
  123. ask_add_field(&mut schema_builder);
  124. if !prompt_yn("Add another field") {
  125. break;
  126. }
  127. }
  128. let schema = schema_builder.build();
  129. let schema_json = format!("{}", json::as_pretty_json(&schema));
  130. println!("\n{}\n", Style::new().fg(Green).paint(schema_json));
  131. let mut index = try!(Index::create(&directory, schema));
  132. index.save_metas()
  133. }