├── .gitignore ├── LICENSE ├── README.md ├── _config.yml ├── defaultdb.go ├── gorganizer-logo-50.jpg ├── gorganizer-logo.png ├── gorganizer.go ├── ini.go └── rules.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Go template 3 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 4 | *.o 5 | *.a 6 | *.so 7 | 8 | # Folders 9 | _obj 10 | _test 11 | 12 | # Architecture specific extensions/prefixes 13 | *.[568vq] 14 | [568vq].out 15 | 16 | *.cgo1.go 17 | *.cgo2.c 18 | _cgo_defun.c 19 | _cgo_gotypes.go 20 | _cgo_export.* 21 | 22 | _testmain.go 23 | 24 | *.exe 25 | *.test 26 | *.prof 27 | ### Linux template 28 | *~ 29 | 30 | # temporary files which can be created if a process still has a handle open of a deleted file 31 | .fuse_hidden* 32 | 33 | # KDE directory preferences 34 | .directory 35 | 36 | # Linux trash folder which might appear on any partition or disk 37 | .Trash-* 38 | ### OSX template 39 | *.DS_Store 40 | .AppleDouble 41 | .LSOverride 42 | 43 | # Icon must end with two \r 44 | Icon 45 | 46 | # Thumbnails 47 | ._* 48 | 49 | # Files that might appear in the root of a volume 50 | .DocumentRevisions-V100 51 | .fseventsd 52 | .Spotlight-V100 53 | .TemporaryItems 54 | .Trashes 55 | .VolumeIcon.icns 56 | .com.apple.timemachine.donotpresent 57 | 58 | # Directories potentially created on remote AFP share 59 | .AppleDB 60 | .AppleDesktop 61 | Network Trash Folder 62 | Temporary Items 63 | .apdisk 64 | ### Windows template 65 | # Windows image file caches 66 | Thumbs.db 67 | ehthumbs.db 68 | 69 | # Folder config file 70 | Desktop.ini 71 | 72 | # Recycle Bin used on file shares 73 | $RECYCLE.BIN/ 74 | 75 | # Windows Installer files 76 | *.cab 77 | *.msi 78 | *.msm 79 | *.msp 80 | 81 | # Windows shortcuts 82 | *.lnk 83 | 84 | .gitignore 85 | .idea/ 86 | *.iml 87 | playground/ 88 | *.db 89 | gorganizer 90 | *.ini -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Diego Siqueira 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![Gorganizer](https://rawgit.com/DiSiqueira/Gorganizer/master/gorganizer-logo-50.jpg) 2 | 3 | # Gorganizer ![Language Badge](https://img.shields.io/badge/Language-Go-blue.svg) ![Go Report](https://goreportcard.com/badge/github.com/DiSiqueira/Gorganizer) ![License Badge](https://img.shields.io/badge/License-MIT-blue.svg) ![Status Badge](https://img.shields.io/badge/Status-Beta-brightgreen.svg) 4 | 5 | Gorganizer is a Go program inspired by [Bhrigu Srivastava][bhrigu123] [Classifier Project][classifier]. 6 | 7 | The Gorganizer's goal is to be a perfect tool providing a stupidly easy-to-use and fast program to organize your files based on its extension. 8 | 9 | [bhrigu123]: https://github.com/bhrigu123 10 | [classifier]: https://github.com/bhrigu123/classifier 11 | 12 | ## Project Status 13 | 14 | Gorganizer is on beta. Pull Requests [are welcome](https://github.com/DiSiqueira/Gorganizer#social-coding) 15 | 16 | ![](https://i.imgur.com/2rFfn9i.gif) 17 | ![](https://i.imgur.com/AkgCeMx.jpg) 18 | 19 | ## Features 20 | 21 | - MORE THAN 60 DEFAULT EXTENSIONS!!! 22 | - It's perfect to organize your DOWNLOADS FOLDER 23 | - Instantly organize your files 24 | - CUSTOMIZE to your needs 25 | - EASY to add rules 26 | - Easy to delete default rules 27 | - STUPIDLY [EASY TO USE](https://github.com/DiSiqueira/Gorganizer#usage) 28 | - Very fast start up and response time 29 | - Uses native libs 30 | - Option to organize your files 31 | - Preview changes before moving 32 | - Language support (English, Portuguese and Turkish) 33 | 34 | ## Installation 35 | 36 | ### Option 1: Go Get 37 | 38 | ```bash 39 | $ go get github.com/DiSiqueira/Gorganizer 40 | $ Gorganizer -h 41 | ``` 42 | 43 | ### Option 2: From source 44 | 45 | ```bash 46 | $ go get gopkg.in/ini.v1 47 | $ git clone https://github.com/DiSiqueira/Gorganizer.git 48 | $ cd Gorganizer/ 49 | $ go build *.go 50 | ``` 51 | 52 | ## Usage 53 | 54 | ### Basic usage 55 | 56 | ```bash 57 | # Organize your current directory 58 | $ ./gorganizer 59 | ``` 60 | 61 | ### Only preview, do not make change 62 | 63 | ```bash 64 | # Prints a preview, but do not move 65 | $ ./gorganizer -preview=true 66 | ``` 67 | 68 | ### Recursive mode 69 | 70 | ```bash 71 | $ ./gorganizer -recursive 72 | ``` 73 | 74 | ### Do not organize specific files 75 | 76 | ```bash 77 | # Exclude .pdf and .docx files 78 | $ ./gorganizer -exclude="pdf,docx" 79 | ``` 80 | 81 | ### Specify language (Default: en) 82 | 83 | ```bash 84 | # Set language to Turkish 85 | $ ./gorganizer -language=tr 86 | ``` 87 | 88 | ### Add new rule 89 | 90 | ```bash 91 | # Add .py to Python folder 92 | $ ./gorganizer -newrule=py:Python 93 | ``` 94 | 95 | ### Delete existing rule 96 | 97 | ```bash 98 | # Delete txt rule 99 | $ ./gorganizer -delrule=txt 100 | ``` 101 | 102 | ### Print all rules 103 | 104 | ```bash 105 | # Print all rules 106 | $ ./gorganizer -allrules=true 107 | ``` 108 | 109 | ### Move organized files to another folder 110 | 111 | ```bash 112 | # Run in current directory and move organized files to ~/Downloads 113 | $ ./gorganizer -output=~/Downloads 114 | ``` 115 | 116 | ### Run in other directory 117 | 118 | ```bash 119 | # Run in ~/Downloads 120 | $ ./gorganizer -directory=~/Downloads 121 | ``` 122 | 123 | ### Run in other directory and send organized files to a organized one 124 | 125 | ```bash 126 | # Run in ~/Downloads 127 | $ ./gorganizer -directory=~/Downloads -output=~/Documents 128 | ``` 129 | 130 | ### Show help 131 | 132 | ```bash 133 | $ ./gorganizer -h 134 | ``` 135 | 136 | ## Program Help 137 | 138 | ![](http://image.prntscr.com/image/a7f2e8071d3742cda44149ed9a7c2674.png) 139 | 140 | ## Contributing 141 | 142 | ### Bug Reports & Feature Requests 143 | 144 | Please use the [issue tracker](https://github.com/DiSiqueira/Gorganizer/issues) to report any bugs or file feature requests. 145 | 146 | ### Developing 147 | 148 | PRs are welcome. To begin developing, do this: 149 | 150 | ```bash 151 | $ go get gopkg.in/ini.v1 152 | $ git clone --recursive git@github.com:DiSiqueira/Gorganizer.git 153 | $ cd Gorganizer/ 154 | $ go run *.go 155 | ``` 156 | 157 | ## Social Coding 158 | 159 | 1. Create an issue to discuss about your idea 160 | 2. [Fork it] (https://github.com/DiSiqueira/Gorganizer/fork) 161 | 3. Create your feature branch (`git checkout -b my-new-feature`) 162 | 4. Commit your changes (`git commit -am 'Add some feature'`) 163 | 5. Push to the branch (`git push origin my-new-feature`) 164 | 6. Create a new Pull Request 165 | 7. Profit! :white_check_mark: 166 | 167 | ## License 168 | 169 | The MIT License (MIT) 170 | 171 | Copyright (c) 2013-2017 Diego Siqueira 172 | 173 | Permission is hereby granted, free of charge, to any person obtaining a copy 174 | of this software and associated documentation files (the "Software"), to deal 175 | in the Software without restriction, including without limitation the rights 176 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 177 | copies of the Software, and to permit persons to whom the Software is 178 | furnished to do so, subject to the following conditions: 179 | 180 | The above copyright notice and this permission notice shall be included in 181 | all copies or substantial portions of the Software. 182 | 183 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 184 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 185 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 186 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 187 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 188 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 189 | THE SOFTWARE. 190 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /defaultdb.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os/user" 6 | "path/filepath" 7 | "strings" 8 | 9 | "gopkg.in/ini.v1" 10 | ) 11 | 12 | func testDb(file string) bool { 13 | cfgTest, err := ini.LoadSources(ini.LoadOptions{AllowBooleanKeys: true, Loose: false}, file) 14 | 15 | if err != nil { 16 | return false 17 | } 18 | 19 | cfg = cfgTest 20 | 21 | return true 22 | } 23 | 24 | func initDb() { 25 | 26 | if language == "" { 27 | language = "en" 28 | } 29 | 30 | //test if exist a configFile in the directory 31 | cfgFile = strings.Replace(configFile, "{lang}", language, 1) 32 | if testDb(cfgFile) { 33 | return 34 | } 35 | 36 | //test if exist a configFile in the user's home 37 | currentUser, _ := user.Current() 38 | cfgFile = filepath.Join(currentUser.HomeDir, cfgFile) 39 | if testDb(cfgFile) { 40 | return 41 | } 42 | 43 | //Create a default database in user's home 44 | cfg = ini.Empty() 45 | defaultDb() 46 | 47 | } 48 | 49 | func closeDb() { 50 | err := cfg.SaveTo(cfgFile) 51 | 52 | if err != nil { 53 | panic(err) 54 | } 55 | } 56 | 57 | func defaultDb() { 58 | 59 | lang := langVars() 60 | 61 | fmt.Println("No database found") 62 | fmt.Println("Creating default database") 63 | 64 | //Music 65 | insertRule("mp3:" + lang["music"]) 66 | insertRule("aac:" + lang["music"]) 67 | insertRule("flac:" + lang["music"]) 68 | insertRule("ogg:" + lang["music"]) 69 | insertRule("wma:" + lang["music"]) 70 | insertRule("m4a:" + lang["music"]) 71 | insertRule("aiff:" + lang["music"]) 72 | insertRule("wav:" + lang["music"]) 73 | insertRule("amr:" + lang["music"]) 74 | 75 | //Videos 76 | insertRule("flv:" + lang["videos"]) 77 | insertRule("ogv:" + lang["videos"]) 78 | insertRule("avi:" + lang["videos"]) 79 | insertRule("mp4:" + lang["videos"]) 80 | insertRule("mpg:" + lang["videos"]) 81 | insertRule("mpeg:" + lang["videos"]) 82 | insertRule("3gp:" + lang["videos"]) 83 | insertRule("mkv:" + lang["videos"]) 84 | insertRule("ts:" + lang["videos"]) 85 | insertRule("webm:" + lang["videos"]) 86 | insertRule("vob:" + lang["videos"]) 87 | insertRule("wmv:" + lang["videos"]) 88 | 89 | //Pictures 90 | insertRule("png:" + lang["pictures"]) 91 | insertRule("jpeg:" + lang["pictures"]) 92 | insertRule("gif:" + lang["pictures"]) 93 | insertRule("jpg:" + lang["pictures"]) 94 | insertRule("bmp:" + lang["pictures"]) 95 | insertRule("svg:" + lang["pictures"]) 96 | insertRule("webp:" + lang["pictures"]) 97 | insertRule("psd:" + lang["pictures"]) 98 | insertRule("tiff:" + lang["pictures"]) 99 | 100 | //Archives 101 | insertRule("rar:" + lang["archives"]) 102 | insertRule("zip:" + lang["archives"]) 103 | insertRule("7z:" + lang["archives"]) 104 | insertRule("gz:" + lang["archives"]) 105 | insertRule("bz2:" + lang["archives"]) 106 | insertRule("tar:" + lang["archives"]) 107 | insertRule("dmg:" + lang["archives"]) 108 | insertRule("tgz:" + lang["archives"]) 109 | insertRule("xz:" + lang["archives"]) 110 | insertRule("iso:" + lang["archives"]) 111 | insertRule("cpio:" + lang["archives"]) 112 | 113 | //Documents 114 | insertRule("txt:" + lang["documents"]) 115 | insertRule("pdf:" + lang["documents"]) 116 | insertRule("doc:" + lang["documents"]) 117 | insertRule("docx:" + lang["documents"]) 118 | insertRule("odf:" + lang["documents"]) 119 | insertRule("xls:" + lang["documents"]) 120 | insertRule("xlsv:" + lang["documents"]) 121 | insertRule("xlsx:" + lang["documents"]) 122 | insertRule("ppt:" + lang["documents"]) 123 | insertRule("pptx:" + lang["documents"]) 124 | insertRule("ppsx:" + lang["documents"]) 125 | insertRule("odp:" + lang["documents"]) 126 | insertRule("odt:" + lang["documents"]) 127 | insertRule("ods:" + lang["documents"]) 128 | insertRule("md:" + lang["documents"]) 129 | insertRule("json:" + lang["documents"]) 130 | insertRule("csv:" + lang["documents"]) 131 | 132 | //Books 133 | insertRule("mobi:" + lang["books"]) 134 | insertRule("epub:" + lang["books"]) 135 | insertRule("chm:" + lang["books"]) 136 | 137 | //DEBPackages 138 | insertRule("deb:" + lang["deb_packages"]) 139 | 140 | //Programs 141 | insertRule("exe:" + lang["programs"]) 142 | insertRule("msi:" + lang["programs"]) 143 | 144 | //RPMPackages 145 | insertRule("rpm:" + lang["rpm_packages"]) 146 | 147 | fmt.Println("Default database initialized") 148 | } 149 | 150 | func langVars() map[string]string { 151 | 152 | lang := make(map[string]string) 153 | 154 | switch language { 155 | case "pt": 156 | lang["music"] = "Musicas" 157 | lang["videos"] = "Videos" 158 | lang["pictures"] = "Imagens" 159 | lang["archives"] = "Arquivos" 160 | lang["documents"] = "Documentos" 161 | lang["books"] = "Livros" 162 | lang["deb_packages"] = "PacotesDEB" 163 | lang["programs"] = "Programas" 164 | lang["rpm_packages"] = "PacotesRPM" 165 | 166 | case "tr": 167 | lang["music"] = "Müzikler" 168 | lang["videos"] = "Videolar" 169 | lang["pictures"] = "Resimler" 170 | lang["archives"] = "Arşivler" 171 | lang["documents"] = "Dokümanlar" 172 | lang["books"] = "Kitaplar" 173 | lang["deb_packages"] = "DEBPaketleri" 174 | lang["programs"] = "Programlar" 175 | lang["rpm_packages"] = "RPMPaketleri" 176 | 177 | default: 178 | lang["music"] = "Music" 179 | lang["videos"] = "Videos" 180 | lang["pictures"] = "Pictures" 181 | lang["archives"] = "Archives" 182 | lang["documents"] = "Documents" 183 | lang["books"] = "Books" 184 | lang["deb_packages"] = "DEBPackages" 185 | lang["programs"] = "Programs" 186 | lang["rpm_packages"] = "RPMPackages" 187 | } 188 | 189 | return lang 190 | } 191 | -------------------------------------------------------------------------------- /gorganizer-logo-50.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d6o/Gorganizer/6896f20fe0c644d0e7b0a6fe39520b1da5722ae6/gorganizer-logo-50.jpg -------------------------------------------------------------------------------- /gorganizer-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d6o/Gorganizer/6896f20fe0c644d0e7b0a6fe39520b1da5722ae6/gorganizer-logo.png -------------------------------------------------------------------------------- /gorganizer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "path" 9 | "path/filepath" 10 | "strings" 11 | 12 | "github.com/disiqueira/gotree" 13 | "gopkg.in/ini.v1" 14 | ) 15 | 16 | const ( 17 | configFile = ".gorganizer-{lang}.ini" 18 | ) 19 | 20 | var cfg *ini.File 21 | var cfgFile string 22 | var language string 23 | 24 | func addToTree(folder, file string, tree gotree.Tree) { 25 | // append to parent, if exists 26 | for _, item := range tree.Items() { 27 | if item.Text() == folder { 28 | item.Add(file) 29 | return 30 | } 31 | } 32 | 33 | // create parent if missing 34 | tree.Add(folder).Add(file) 35 | } 36 | 37 | type excludeListType []string 38 | 39 | var excludeList excludeListType 40 | 41 | func (e excludeListType) checkExclude(ext string) bool { 42 | if ext == "" { 43 | return false 44 | } 45 | for _, item := range e { 46 | if item == ext { 47 | return true 48 | } 49 | } 50 | return false 51 | } 52 | 53 | func main() { 54 | outputFolder := flag.String("output", ".", "Main directory to put organized folders") 55 | inputFolder := flag.String("directory", ".", "The directory whose files to classify") 56 | 57 | newRule := flag.String("newrule", "", "Insert a new rule. Format ext:folder Example: mp3:Music") 58 | delRule := flag.String("delrule", "", "Delete a rule. Format ext Example: mp3") 59 | 60 | printRules := flag.Bool("allrules", false, "Print all rules") 61 | 62 | preview := flag.Bool("preview", false, "Only preview, do not move files") 63 | recursive := flag.Bool("recursive", false, "Search over all directories.") 64 | ignoreHiddenFiles := flag.Bool("hidden", true, "Ignore hidden files") 65 | 66 | excludeExtentions := flag.String("exclude", "", "Exclude files will ignore files for organizer. Format pdf,odt") 67 | 68 | flag.StringVar(&language, "language", "en", "Specify language: en|tr|pt") 69 | 70 | flag.Parse() 71 | 72 | initDb() 73 | 74 | defer closeDb() 75 | 76 | excludeList = strings.Split(*excludeExtentions, ",") 77 | 78 | if len(*newRule) > 0 { 79 | fmt.Println("Creating new rule") 80 | err := insertRule(*newRule) 81 | if err != nil { 82 | fmt.Println(err) 83 | return 84 | } 85 | showRules() 86 | return 87 | } 88 | 89 | if len(*delRule) > 0 { 90 | fmt.Println("Deleting rule") 91 | deleteRule(*delRule) 92 | showRules() 93 | return 94 | } 95 | 96 | if *printRules { 97 | showRules() 98 | return 99 | } 100 | 101 | fmt.Println("GOrganizing your Files") 102 | 103 | tree := gotree.New("Files") 104 | 105 | scanDirectory(*inputFolder, *outputFolder, tree, *preview, *recursive, *ignoreHiddenFiles) 106 | 107 | fmt.Println(tree.Print()) 108 | 109 | fmt.Println("All files have been GOrganized!") 110 | } 111 | 112 | func scanDirectory(inputFolder, outputFolder string, tree gotree.Tree, preview, recursive, ignoreHiddenFiles bool) { 113 | files, _ := ioutil.ReadDir(inputFolder) 114 | for _, f := range files { 115 | if strings.Index(f.Name(), ".") == 0 && !ignoreHiddenFiles { 116 | addToTree("Hidden Files", f.Name(), tree) 117 | continue 118 | } 119 | if f.IsDir() && recursive { 120 | scanDirectory(filepath.Join(inputFolder, f.Name()), outputFolder, tree, preview, recursive, ignoreHiddenFiles) 121 | } 122 | 123 | file := filepath.Join(inputFolder, f.Name()) 124 | ext := strings.TrimPrefix(path.Ext(file), ".") 125 | 126 | if excludeList.checkExclude(ext) { 127 | addToTree("Excluded Files", f.Name(), tree) 128 | continue 129 | } 130 | 131 | newFolder := iniGet(ext) 132 | 133 | if len(newFolder) > 0 { 134 | 135 | folder := filepath.Join(outputFolder, newFolder) 136 | newFile := filepath.Join(folder, f.Name()) 137 | 138 | if !preview { 139 | _ = os.Mkdir(folder, os.ModePerm) 140 | os.Rename(file, newFile) 141 | } 142 | } else { 143 | newFolder = "Unknown extension (will not be moved)" 144 | } 145 | 146 | addToTree(newFolder, f.Name(), tree) 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /ini.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/disiqueira/gotree" 7 | "fmt" 8 | ) 9 | 10 | func iniGet(key string) string { 11 | key = strings.ToLower(key) 12 | names := cfg.SectionStrings() 13 | 14 | for _, section := range names[1:] { 15 | if cfg.Section(section).HasKey(key) { 16 | return section 17 | } 18 | } 19 | 20 | return "" 21 | } 22 | 23 | func iniSet(key, value string) error { 24 | title := strings.Title(value) 25 | key = strings.ToLower(key) 26 | 27 | _, err := cfg.Section(title).NewKey(key, "") 28 | 29 | return err 30 | } 31 | 32 | func iniDelete(key string) bool { 33 | names := cfg.SectionStrings() 34 | 35 | for _, section := range names { 36 | if cfg.Section(section).HasKey(key) { 37 | cfg.Section(section).DeleteKey(key) 38 | return true 39 | } 40 | } 41 | 42 | return false 43 | } 44 | 45 | func iniScanExt() { 46 | names := cfg.SectionStrings() 47 | tree := gotree.New("Rules") 48 | 49 | for _, section := range names[1:] { 50 | folder := tree.Add(section) 51 | keys := cfg.Section(section).KeyStrings() 52 | for _, key := range keys { 53 | folder.Add(key) 54 | } 55 | } 56 | 57 | fmt.Println(tree.Print()) 58 | } 59 | -------------------------------------------------------------------------------- /rules.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func insertRule(rule string) error { 9 | ruleArr := strings.Split(rule, ":") 10 | if len(ruleArr) != 2 { 11 | return fmt.Errorf("rule must have exactly one colon ':', found %d in %#v", strings.Count(rule, ":"), rule) 12 | } 13 | if len(ruleArr[0]) == 0 || len(ruleArr[1]) == 0 { 14 | return fmt.Errorf("rule must be in the format 'extension:folder', got %#v", rule) 15 | } 16 | 17 | return iniSet(ruleArr[0], ruleArr[1]) 18 | } 19 | 20 | func deleteRule(rule string) { 21 | iniDelete(rule) 22 | } 23 | 24 | func showRules() { 25 | iniScanExt() 26 | } 27 | --------------------------------------------------------------------------------