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.

152 lines
5.0KB

  1. use clap::ArgMatches;
  2. use std::convert::From;
  3. use std::path::PathBuf;
  4. use tantivy;
  5. use tantivy::schema::Cardinality;
  6. use tantivy::schema::*;
  7. use tantivy::Index;
  8. use std::io;
  9. use ansi_term::Style;
  10. use ansi_term::Colour::{Red, Blue, Green};
  11. use std::io::Write;
  12. use serde_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().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::default();
  66. if prompt_yn("Should the field be stored") {
  67. text_options = text_options.set_stored();
  68. }
  69. if prompt_yn("Should the field be indexed") {
  70. let mut text_indexing_options = TextFieldIndexing
  71. ::default()
  72. .set_index_option(IndexRecordOption::Basic)
  73. .set_tokenizer("en_stem");
  74. if prompt_yn("Should the term be tokenized?") {
  75. if prompt_yn("Should the term frequencies (per doc) be in the index") {
  76. if prompt_yn("Should the term positions (per doc) be in the index") {
  77. text_indexing_options = text_indexing_options.set_index_option(IndexRecordOption::WithFreqsAndPositions);
  78. } else {
  79. text_indexing_options = text_indexing_options.set_index_option(IndexRecordOption::WithFreqs);
  80. }
  81. }
  82. } else {
  83. text_indexing_options = text_indexing_options.set_tokenizer("raw");
  84. }
  85. text_options = text_options.set_indexing_options(text_indexing_options);
  86. }
  87. schema_builder.add_text_field(field_name, text_options);
  88. }
  89. fn ask_add_field_u64(field_name: &str, schema_builder: &mut SchemaBuilder) {
  90. let mut u64_options = IntOptions::default();
  91. if prompt_yn("Should the field be stored") {
  92. u64_options = u64_options.set_stored();
  93. }
  94. if prompt_yn("Should the field be fast") {
  95. u64_options = u64_options.set_fast(Cardinality::SingleValue);
  96. }
  97. if prompt_yn("Should the field be indexed") {
  98. u64_options = u64_options.set_indexed();
  99. }
  100. schema_builder.add_u64_field(field_name, u64_options);
  101. }
  102. fn ask_add_field(schema_builder: &mut SchemaBuilder) {
  103. println!("\n\n");
  104. let field_name = prompt_input("New field name ", field_name_validate);
  105. let text_or_integer = prompt_options("Text or unsigned 32-bit integer", vec!('T', 'I'));
  106. if text_or_integer =='T' {
  107. ask_add_field_text(&field_name, schema_builder);
  108. }
  109. else {
  110. ask_add_field_u64(&field_name, schema_builder);
  111. }
  112. }
  113. fn run_new(directory: PathBuf) -> tantivy::Result<()> {
  114. println!("\n{} ", Style::new().bold().fg(Green).paint("Creating new index"));
  115. println!("{} ", Style::new().bold().fg(Green).paint("Let's define it's schema!"));
  116. let mut schema_builder = SchemaBuilder::default();
  117. loop {
  118. ask_add_field(&mut schema_builder);
  119. if !prompt_yn("Add another field") {
  120. break;
  121. }
  122. }
  123. let schema = schema_builder.build();
  124. let schema_json = format!("{}", serde_json::to_string_pretty(&schema).unwrap());
  125. println!("\n{}\n", Style::new().fg(Green).paint(schema_json));
  126. Index::create_in_dir(&directory, schema)?;
  127. Ok(())
  128. }