diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/bin/jvn_comp.rs | 220 | ||||
| -rw-r--r-- | src/cmds/comp/helpdoc.rs | 22 | ||||
| -rw-r--r-- | src/cmds/comp/sheetdump.rs | 16 | ||||
| -rw-r--r-- | src/cmds/comp/sheetedit.rs | 17 | ||||
| -rw-r--r-- | src/cmds/comp/version.rs | 17 | ||||
| -rw-r--r-- | src/cmds/comp/workspace_alias.rs | 24 | ||||
| -rw-r--r-- | src/cmds/comp/workspace_sheet.rs | 46 | ||||
| -rw-r--r-- | src/systems/comp.rs | 1 | ||||
| -rw-r--r-- | src/systems/comp/result.rs | 205 |
9 files changed, 460 insertions, 108 deletions
diff --git a/src/bin/jvn_comp.rs b/src/bin/jvn_comp.rs index de52253..d0b258a 100644 --- a/src/bin/jvn_comp.rs +++ b/src/bin/jvn_comp.rs @@ -1,46 +1,69 @@ use std::{fs::OpenOptions, process::exit}; use clap::Parser; +use cli_utils::env::locales::current_locales; +use comp_system_macros::{file_suggest, suggest}; use env_logger::Target; use jvcli::systems::{ cmd::_commands::jv_cmd_nodes, comp::{ _comps::{jv_cmd_comp_nodes, match_comp}, - context::CompletionContext, + context::{CompletionContext, ShellFlag}, + result::{CompletionResult, CompletionSuggestion}, }, render::renderer::jv_override_renderers, }; #[cfg(debug_assertions)] use log::debug; +#[cfg(debug_assertions)] use log::{LevelFilter, error, trace}; +use rust_i18n::{set_locale, t}; + +rust_i18n::i18n!("resources/locales/jvn", fallback = "en"); + +macro_rules! global_flags_suggest { + () => { + suggest!( + "--confirm" = t!("global_flag.confirm").trim(), + "-C" = t!("global_flag.confirm").trim(), + "--help" = t!("global_flag.help").trim(), + "-h" = t!("global_flag.help").trim(), + "--lang" = t!("global_flag.lang").trim(), + "--no-error-logs" = t!("global_flag.no_error_logs").trim(), + "--no-progress" = t!("global_flag.no_progress").trim(), + "--quiet" = t!("global_flag.quiet").trim(), + "-q" = t!("global_flag.quiet").trim(), + "--renderer" = t!("global_flag.renderer").trim(), + "--verbose" = t!("global_flag.verbose").trim(), + "-V" = t!("global_flag.verbose").trim(), + "--version" = t!("global_flag.version").trim(), + "-v" = t!("global_flag.version").trim(), + ) + .into() + }; +} -const GLOBAL_FLAGS: &[&str] = &[ - "--confirm", - "-C", - "--help", - "-h", - "--lang", - "--no-error-logs", - "--no-progress", - "--quiet", - "-q", - "--renderer", - "--verbose", - "-V", - "--version", - "-v", -]; - -const LANGUAGES: [&str; 2] = [ - "en", // English - "zh-CN", // 简体中文 -]; +macro_rules! language_suggest { + () => { + // Sort in A - Z order + suggest!( + // English + "en" = "English", + // Simplified Chinese + "zh-CN" = "简体中文" + ) + .into() + }; +} fn main() { // If not in release mode, initialize env_logger to capture logs #[cfg(debug_assertions)] init_env_logger(); + let lang = current_locales(); + set_locale(&lang); + // Check if help flag is present in arguments let args: Vec<String> = std::env::args().collect(); if args.iter().any(|arg| arg == "-h" || arg == "--help") { @@ -68,6 +91,7 @@ fn main() { } Err(e) => { // An error occurred, collecting information for output + #[cfg(debug_assertions)] error!( "Error: {}, origin=\"{}\"", e, @@ -81,42 +105,56 @@ fn main() { #[cfg(debug_assertions)] trace_ctx(&ctx); - trace!("Try using specific completion"); - let specific_result = comp(&ctx); - trace!("Using default completion"); + #[cfg(debug_assertions)] + trace!("Generate specific completion"); + let specific_result = specific_comp(&ctx); + + #[cfg(debug_assertions)] + trace!("Generate default completion"); let default_result = default_comp(&ctx); + #[cfg(debug_assertions)] + trace!("specific_result: {}", specific_result.to_string()); + #[cfg(debug_assertions)] + trace!("default_result: {}", default_result.to_string()); + let combined_result = match (specific_result, default_result) { - (None, None) => None, - (Some(s), None) => Some(s), - (None, Some(d)) => Some(d), - (Some(mut s), Some(d)) => { + (CompletionResult::FileCompletion, CompletionResult::FileCompletion) => { + CompletionResult::file_comp() + } + (CompletionResult::Suggestions(s), CompletionResult::FileCompletion) => { + CompletionResult::Suggestions(s) + } + (CompletionResult::FileCompletion, CompletionResult::Suggestions(d)) => { + CompletionResult::Suggestions(d) + } + (CompletionResult::Suggestions(mut s), CompletionResult::Suggestions(d)) => { s.extend(d); - Some(s) + CompletionResult::Suggestions(s) } }; - handle_comp_result(&combined_result); + handle_comp_result(combined_result, &ctx); } -fn default_comp(ctx: &CompletionContext) -> Option<Vec<String>> { +fn default_comp(ctx: &CompletionContext) -> CompletionResult { if ctx.current_word.starts_with('-') { - return Some(GLOBAL_FLAGS.iter().map(|s| s.to_string()).collect()); + return global_flags_suggest!(); } // Match and comp Override Renderers if ctx.previous_word == "--renderer" { - return Some(jv_override_renderers()); + return jv_override_renderers().into(); } if ctx.previous_word == "--lang" { - return Some(LANGUAGES.iter().map(|s| s.to_string()).collect()); + return language_suggest!(); } - None + file_suggest!() } -fn comp(ctx: &CompletionContext) -> Option<Vec<String>> { +fn specific_comp(ctx: &CompletionContext) -> CompletionResult { let args: Vec<String> = ctx.all_words.iter().skip(1).cloned().collect(); let nodes = jv_cmd_comp_nodes(); let command = format!("{} ", args.join(" ")); @@ -137,23 +175,39 @@ fn comp(ctx: &CompletionContext) -> Option<Vec<String>> { let match_node: Option<String> = match matching_nodes.len() { 0 => { - if let Some(result) = try_comp_cmd_nodes(ctx) { - return Some(result); + #[cfg(debug_assertions)] + trace!("No matching nodes found, trying command nodes"); + let r = try_comp_cmd_nodes(ctx); + if r.is_suggestion() { + #[cfg(debug_assertions)] + trace!("try_comp_cmd_nodes returned suggestions"); + return r; } + #[cfg(debug_assertions)] + trace!("try_comp_cmd_nodes returned file completion"); // No matching node found None } 1 => { // Single matching node found + #[cfg(debug_assertions)] + trace!("Single matching node found: {}", matching_nodes[0]); Some(matching_nodes[0].clone()) } _ => { // Multiple matching nodes found // Find the node with the longest length (most specific match) - matching_nodes + #[cfg(debug_assertions)] + trace!("Multiple matching nodes found: {:?}", matching_nodes); + let longest_node = matching_nodes .iter() .max_by_key(|node| node.len()) - .map(|node| node.to_string()) + .map(|node| node.to_string()); + #[cfg(debug_assertions)] + if let Some(ref node) = longest_node { + trace!("Selected longest node: {}", node); + } + longest_node } }; @@ -163,17 +217,29 @@ fn comp(ctx: &CompletionContext) -> Option<Vec<String>> { None => trace!("No completions matched."), } - let match_node = match_node?; + let match_node = match match_node { + Some(node) => node, + None => { + #[cfg(debug_assertions)] + trace!("No match node found, returning file completion"); + return file_suggest!(); + } + }; - match_comp(match_node, ctx.clone()) + #[cfg(debug_assertions)] + trace!("Calling match_comp with node: {}", match_node); + let result = match_comp(match_node, ctx.clone()); + #[cfg(debug_assertions)] + trace!("match_comp returned: {}", result.to_string()); + result } -fn try_comp_cmd_nodes(ctx: &CompletionContext) -> Option<Vec<String>> { +fn try_comp_cmd_nodes(ctx: &CompletionContext) -> CompletionResult { let cmd_nodes = jv_cmd_nodes(); // If the current position is less than 1, do not perform completion if ctx.word_index < 1 { - return None; + return file_suggest!(); }; // Get the current input path @@ -226,7 +292,7 @@ fn try_comp_cmd_nodes(ctx: &CompletionContext) -> Option<Vec<String>> { "try_comp_cmd_nodes: current word suggestions = {:?}", suggestions ); - return Some(suggestions); + return suggestions.into(); } } @@ -279,28 +345,68 @@ fn try_comp_cmd_nodes(ctx: &CompletionContext) -> Option<Vec<String>> { debug!("try_comp_cmd_nodes: suggestions = {:?}", suggestions); if suggestions.is_empty() { - None + file_suggest!() } else { - Some(suggestions) + suggestions.into() } } -fn handle_comp_result(r: &Option<Vec<String>>) { +fn handle_comp_result(r: CompletionResult, ctx: &CompletionContext) { match r { - Some(suggestions) => { - suggestions - .iter() - .for_each(|suggest| println!("{}", suggest)); - exit(0) - } - None => { - // Output "_file_" to notify the completion script to perform "file completion" + CompletionResult::FileCompletion => { println!("_file_"); exit(0) } + CompletionResult::Suggestions(suggestions) => match ctx.shell_flag { + ShellFlag::Zsh => print_suggest_with_description(suggestions), + ShellFlag::Fish => print_suggest_with_description_fish(suggestions), + _ => print_suggest(suggestions), + }, } } +fn print_suggest(mut suggestions: Vec<CompletionSuggestion>) { + suggestions.sort(); + #[cfg(debug_assertions)] + trace!("print_suggest suggestions: {:?}", suggestions); + suggestions + .iter() + .for_each(|suggest| println!("{}", suggest.suggest)); + exit(0) +} + +fn print_suggest_with_description(mut suggestions: Vec<CompletionSuggestion>) { + suggestions.sort(); + #[cfg(debug_assertions)] + trace!( + "print_suggest_with_description suggestions: {:?}", + suggestions + ); + suggestions + .iter() + .for_each(|suggest| match &suggest.description { + Some(desc) => println!("{}$({})", suggest.suggest, desc), + None => println!("{}", suggest.suggest), + }); + exit(0) +} + +fn print_suggest_with_description_fish(mut suggestions: Vec<CompletionSuggestion>) { + suggestions.sort(); + #[cfg(debug_assertions)] + trace!( + "print_suggest_with_description_fish suggestions: {:?}", + suggestions + ); + suggestions + .iter() + .for_each(|suggest| match &suggest.description { + Some(desc) => println!("{}\t{}", suggest.suggest, desc), + None => println!("{}", suggest.suggest), + }); + exit(0) +} + #[cfg(debug_assertions)] fn trace_ctx(ctx: &CompletionContext) { log::trace!("command_line={}", ctx.command_line); diff --git a/src/cmds/comp/helpdoc.rs b/src/cmds/comp/helpdoc.rs index 7f07cad..423e2bf 100644 --- a/src/cmds/comp/helpdoc.rs +++ b/src/cmds/comp/helpdoc.rs @@ -1,13 +1,17 @@ -use crate::systems::{comp::context::CompletionContext, helpdoc}; +use comp_system_macros::file_suggest; -pub fn comp(ctx: CompletionContext) -> Option<Vec<String>> { +use crate::systems::{ + comp::{context::CompletionContext, result::CompletionResult}, + helpdoc, +}; + +pub fn comp(ctx: CompletionContext) -> CompletionResult { if ctx.previous_word == "helpdoc" { - return Some( - helpdoc::get_helpdoc_list() - .iter() - .map(|s| s.to_string()) - .collect(), - ); + return helpdoc::get_helpdoc_list() + .iter() + .map(|s| s.to_string()) + .collect::<Vec<String>>() + .into(); } - None + file_suggest!() } diff --git a/src/cmds/comp/sheetdump.rs b/src/cmds/comp/sheetdump.rs index 3528cf3..e3105c0 100644 --- a/src/cmds/comp/sheetdump.rs +++ b/src/cmds/comp/sheetdump.rs @@ -1,11 +1,15 @@ -use cli_utils::string_vec; +use comp_system_macros::{file_suggest, suggest}; +use rust_i18n::t; -use crate::systems::comp::context::CompletionContext; +use crate::systems::comp::{context::CompletionContext, result::CompletionResult}; -pub fn comp(ctx: CompletionContext) -> Option<Vec<String>> { +pub fn comp(ctx: CompletionContext) -> CompletionResult { if ctx.current_word.starts_with('-') { - return Some(string_vec!["--no-sort", "--no-pretty"]); + return suggest!( + "--no-sort" = t!("sheetdump.comp.no_sort").trim(), + "--no-pretty" = t!("sheetdump.comp.no_pretty").trim() + ) + .into(); } - - None + file_suggest!() } diff --git a/src/cmds/comp/sheetedit.rs b/src/cmds/comp/sheetedit.rs index d210028..1ebf63d 100644 --- a/src/cmds/comp/sheetedit.rs +++ b/src/cmds/comp/sheetedit.rs @@ -1,15 +1,20 @@ -use cli_utils::string_vec; +use comp_system_macros::{file_suggest, suggest}; +use rust_i18n::t; -use crate::systems::comp::context::CompletionContext; +use crate::systems::comp::{context::CompletionContext, result::CompletionResult}; -pub fn comp(ctx: CompletionContext) -> Option<Vec<String>> { +pub fn comp(ctx: CompletionContext) -> CompletionResult { if ctx.current_word.starts_with('-') { - return Some(string_vec!["-e", "--editor"]); + return suggest!( + "-e" = t!("sheetedit.comp.editor").trim(), + "--editor" = t!("sheetedit.comp.editor").trim(), + ) + .into(); } if ctx.previous_word == "-e" || ctx.previous_word == "--editor" { - return Some(vec![]); + return suggest!().into(); } - None + file_suggest!() } diff --git a/src/cmds/comp/version.rs b/src/cmds/comp/version.rs index 1460214..2c6b674 100644 --- a/src/cmds/comp/version.rs +++ b/src/cmds/comp/version.rs @@ -1,11 +1,16 @@ -use cli_utils::string_vec; +use comp_system_macros::{file_suggest, suggest}; +use rust_i18n::t; -use crate::systems::comp::context::CompletionContext; +use crate::systems::comp::{context::CompletionContext, result::CompletionResult}; -pub fn comp(ctx: CompletionContext) -> Option<Vec<String>> { +pub fn comp(ctx: CompletionContext) -> CompletionResult { if ctx.current_word.starts_with('-') { - return Some(string_vec!["-c", "--with-compile-info", "--no-banner"]); + return suggest!( + "-c" = t!("version.comp.with_compile_info").trim(), + "--with-compile-info" = t!("version.comp.with_compile_info").trim(), + "--no-banner" = t!("version.comp.no_banner").trim() + ) + .into(); } - - None + file_suggest!() } diff --git a/src/cmds/comp/workspace_alias.rs b/src/cmds/comp/workspace_alias.rs index a8ac495..cd39c9d 100644 --- a/src/cmds/comp/workspace_alias.rs +++ b/src/cmds/comp/workspace_alias.rs @@ -1,17 +1,25 @@ -use cli_utils::string_vec; +use comp_system_macros::{file_suggest, suggest}; +use rust_i18n::t; -use crate::systems::comp::context::CompletionContext; +use crate::systems::comp::{context::CompletionContext, result::CompletionResult}; -pub fn comp(ctx: CompletionContext) -> Option<Vec<String>> { +pub fn comp(ctx: CompletionContext) -> CompletionResult { if ctx.current_word.starts_with('-') { - return Some(string_vec![ - "-i", "--insert", "-Q", "--query", "-e", "--erase", "--to", - ]); + return suggest!( + "-i" = t!("workspace_alias.comp.insert").trim(), + "--insert" = t!("workspace_alias.comp.insert").trim(), + "-Q" = t!("workspace_alias.comp.query").trim(), + "--query" = t!("workspace_alias.comp.query").trim(), + "-e" = t!("workspace_alias.comp.erase").trim(), + "--erase" = t!("workspace_alias.comp.erase").trim(), + "--to" = t!("workspace_alias.comp.to").trim() + ) + .into(); } if ctx.previous_word == "--to" { - return Some(vec![]); + return suggest!().into(); } - None + file_suggest!() } diff --git a/src/cmds/comp/workspace_sheet.rs b/src/cmds/comp/workspace_sheet.rs index 3162442..8318fbe 100644 --- a/src/cmds/comp/workspace_sheet.rs +++ b/src/cmds/comp/workspace_sheet.rs @@ -1,25 +1,39 @@ -use crate::systems::comp::context::CompletionContext; -use cli_utils::string_vec; +use comp_system_macros::{file_suggest, suggest}; use just_enough_vcs::system::workspace::workspace::manager::WorkspaceManager; +use rust_i18n::t; -pub fn comp(ctx: CompletionContext) -> Option<Vec<String>> { +use crate::systems::comp::{context::CompletionContext, result::CompletionResult}; + +pub fn comp(ctx: CompletionContext) -> CompletionResult { if ctx.current_word.starts_with('-') { - return Some(string_vec![ - "-A", - "--list-all", - "-p", - "--print-path", - "-n", - "--new", - "-d", - "--delete", - ]); + return suggest!( + "-A" = t!("workspace_sheet.comp.list_all").trim(), + "--list-all" = t!("workspace_sheet.comp.list_all").trim(), + "-p" = t!("workspace_sheet.comp.print_path").trim(), + "--print-path" = t!("workspace.sheet.comp.print_path").trim(), + "-n" = t!("workspace_sheet.comp.new").trim(), + "--new" = t!("workspace_sheet.comp.new").trim(), + "-d" = t!("workspace_sheet.comp.delete").trim(), + "--delete" = t!("workspace_sheet.comp.delete").trim() + ) + .into(); } if ctx.previous_word == "--new" || ctx.previous_word == "-n" { - return Some(vec![]); + return suggest!().into(); + } + + if ctx.previous_word == "--list-all" + || ctx.previous_word == "-A" + || ctx.previous_word == "--print-path" + || ctx.previous_word == "-p" + || ctx.previous_word == "--delete" + || ctx.previous_word == "-d" + { + let rt = tokio::runtime::Runtime::new().unwrap(); + let names = rt.block_on(WorkspaceManager::new().list_sheet_names()); + return names.into(); } - let rt = tokio::runtime::Runtime::new().unwrap(); - Some(rt.block_on(WorkspaceManager::new().list_sheet_names())) + file_suggest!() } diff --git a/src/systems/comp.rs b/src/systems/comp.rs index c7c6577..077fb7f 100644 --- a/src/systems/comp.rs +++ b/src/systems/comp.rs @@ -1,2 +1,3 @@ pub mod _comps; pub mod context; +pub mod result; diff --git a/src/systems/comp/result.rs b/src/systems/comp/result.rs new file mode 100644 index 0000000..e64021d --- /dev/null +++ b/src/systems/comp/result.rs @@ -0,0 +1,205 @@ +pub enum CompletionResult { + FileCompletion, + Suggestions(Vec<CompletionSuggestion>), +} + +impl CompletionResult { + /// Creates a `CompletionResult` representing file completion. + pub fn file_comp() -> Self { + CompletionResult::FileCompletion + } + + /// Creates a `CompletionResult` with an empty suggestions list. + pub fn empty_comp() -> CompletionSuggestInsert { + CompletionSuggestInsert { + suggestions: Vec::new(), + } + } + + /// Returns `true` if this is a `FileCompletion` variant. + pub fn is_file(&self) -> bool { + matches!(self, Self::FileCompletion) + } + + /// Returns `true` if this is a `Suggestions` variant. + pub fn is_suggestion(&self) -> bool { + matches!(self, Self::Suggestions(_)) + } + + /// Returns a reference to the suggestions vector if this is a `Suggestions` variant, + /// otherwise returns `None`. + pub fn suggestion(&self) -> Option<&Vec<CompletionSuggestion>> { + match self { + Self::FileCompletion => None, + Self::Suggestions(v) => Some(v), + } + } + + /// Returns the unit value if this is a `FileCompletion` variant, otherwise panics. + pub fn unwrap_file(&self) { + match self { + Self::FileCompletion => (), + Self::Suggestions(_) => { + panic!("called `CompletionResult::unwrap_file()` on a `Suggestions` value") + } + } + } + + /// Returns a reference to the suggestions vector if this is a `Suggestions` variant, + /// otherwise panics. + pub fn unwrap_suggestion(&self) -> &Vec<CompletionSuggestion> { + match self { + Self::FileCompletion => { + panic!("called `CompletionResult::unwrap_suggestion()` on a `FileCompletion` value") + } + Self::Suggestions(v) => v, + } + } + + /// Returns the unit value if this is a `FileCompletion` variant, otherwise returns `default`. + pub fn unwrap_file_or(&self, default: ()) { + match self { + Self::FileCompletion => (), + Self::Suggestions(_) => default, + } + } + + /// Returns a reference to the suggestions vector if this is a `Suggestions` variant, + /// otherwise returns `default`. + pub fn unwrap_suggestion_or<'a>( + &'a self, + default: &'a Vec<CompletionSuggestion>, + ) -> &'a Vec<CompletionSuggestion> { + match self { + Self::FileCompletion => default, + Self::Suggestions(v) => v, + } + } + + /// Returns the unit value if this is a `FileCompletion` variant, otherwise calls `f`. + pub fn unwrap_file_or_else<F>(&self, f: F) + where + F: FnOnce(), + { + match self { + Self::FileCompletion => (), + Self::Suggestions(_) => f(), + } + } + + /// Returns a reference to the suggestions vector if this is a `Suggestions` variant, + /// otherwise calls `f`. + pub fn unwrap_suggestion_or_else<'a, F>(&'a self, f: F) -> &'a Vec<CompletionSuggestion> + where + F: FnOnce() -> &'a Vec<CompletionSuggestion>, + { + match self { + Self::FileCompletion => f(), + Self::Suggestions(v) => v, + } + } +} + +pub struct CompletionSuggestInsert { + suggestions: Vec<CompletionSuggestion>, +} + +impl CompletionSuggestInsert { + /// Adds a suggestion with the given text and no description. + pub fn with_suggest<S: Into<String>>(mut self, suggest: S) -> Self { + self.suggestions.push(CompletionSuggestion { + suggest: suggest.into(), + description: None, + }); + self + } + + /// Adds a suggestion with the given text and description. + pub fn with_suggest_desc<S: Into<String>, D: Into<String>>( + mut self, + suggest: S, + description: D, + ) -> Self { + self.suggestions.push(CompletionSuggestion { + suggest: suggest.into(), + description: Some(description.into()), + }); + self + } +} + +impl From<CompletionSuggestInsert> for CompletionResult { + fn from(insert: CompletionSuggestInsert) -> Self { + CompletionResult::Suggestions(insert.suggestions) + } +} + +impl From<Vec<String>> for CompletionResult { + fn from(strings: Vec<String>) -> Self { + CompletionResult::Suggestions( + strings + .into_iter() + .map(|s| CompletionSuggestion { + suggest: s, + description: None, + }) + .collect(), + ) + } +} + +impl std::fmt::Display for CompletionResult { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CompletionResult::FileCompletion => write!(f, "_file_"), + CompletionResult::Suggestions(suggestions) => { + let suggestions_str: Vec<String> = + suggestions.iter().map(|s| s.suggest.clone()).collect(); + write!(f, "{}", suggestions_str.join(", ")) + } + } + } +} + +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct CompletionSuggestion { + pub suggest: String, + pub description: Option<String>, +} + +impl std::ops::Deref for CompletionSuggestion { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.suggest + } +} + +impl std::ops::DerefMut for CompletionSuggestion { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.suggest + } +} + +impl AsRef<str> for CompletionSuggestion { + fn as_ref(&self) -> &str { + &self.suggest + } +} + +impl PartialOrd for CompletionSuggestion { + fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for CompletionSuggestion { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + match (&self.description, &other.description) { + (None, None) => self.suggest.cmp(&other.suggest), + (Some(_), None) => std::cmp::Ordering::Less, + (None, Some(_)) => std::cmp::Ordering::Greater, + (Some(_), Some(_)) => self.suggest.cmp(&other.suggest), + } + } +} |
