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.

151 lines
4.9KB

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