github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/advisor/cmdfinder.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2018 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package advisor 21 22 import ( 23 "os" 24 ) 25 26 type Command struct { 27 Snap string 28 Version string `json:"Version,omitempty"` 29 Command string 30 } 31 32 func FindCommand(command string) ([]Command, error) { 33 finder, err := newFinder() 34 if err != nil && os.IsNotExist(err) { 35 return nil, nil 36 } 37 if err != nil { 38 return nil, err 39 } 40 defer finder.Close() 41 42 return finder.FindCommand(command) 43 } 44 45 const ( 46 minLen = 3 47 maxLen = 256 48 ) 49 50 // based on CommandNotFound.py:similar_words.py 51 func similarWords(word string) []string { 52 const alphabet = "abcdefghijklmnopqrstuvwxyz-_0123456789" 53 similar := make(map[string]bool, 2*len(word)+2*len(word)*len(alphabet)) 54 55 // deletes 56 for i := range word { 57 similar[word[:i]+word[i+1:]] = true 58 } 59 // transpose 60 for i := 0; i < len(word)-1; i++ { 61 similar[word[:i]+word[i+1:i+2]+word[i:i+1]+word[i+2:]] = true 62 } 63 // replaces 64 for i := range word { 65 for _, r := range alphabet { 66 similar[word[:i]+string(r)+word[i+1:]] = true 67 } 68 } 69 // inserts 70 for i := range word { 71 for _, r := range alphabet { 72 similar[word[:i]+string(r)+word[i:]] = true 73 } 74 } 75 76 // convert for output 77 ret := make([]string, 0, len(similar)) 78 for w := range similar { 79 ret = append(ret, w) 80 } 81 82 return ret 83 } 84 85 func FindMisspelledCommand(command string) ([]Command, error) { 86 if len(command) < minLen || len(command) > maxLen { 87 return nil, nil 88 } 89 finder, err := newFinder() 90 if err != nil && os.IsNotExist(err) { 91 return nil, nil 92 } 93 if err != nil { 94 return nil, err 95 } 96 defer finder.Close() 97 98 alternatives := make([]Command, 0, 32) 99 for _, w := range similarWords(command) { 100 res, err := finder.FindCommand(w) 101 if err != nil { 102 return nil, err 103 } 104 if len(res) > 0 { 105 alternatives = append(alternatives, res...) 106 } 107 } 108 109 return alternatives, nil 110 }