diff options
Diffstat (limited to 'lettersnail.go')
-rw-r--r-- | lettersnail.go | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/lettersnail.go b/lettersnail.go new file mode 100644 index 0000000..0a211fd --- /dev/null +++ b/lettersnail.go @@ -0,0 +1,197 @@ +/* lettersnail.go: main program + * + * Copyright (C) 2016-2018 Clemens Fries <github-lettersnail@xenoworld.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package main + +import ( + "fmt" + "github.com/docopt/docopt.go" + "os" + "os/user" + "path/filepath" + "github.com/githubert/lettersnail/cmd" + . "github.com/githubert/lettersnail/common" +) + + +var usageMain = +// tag::main[] +` +Usage: + lettersnail [options] <command> [<args>...] + +Options: + --workdir=WORKDIR Working directory, defaults to $HOME/lettersnail + --config=CONFIG Use the given INI file instead of .config/lettersnail.ini + --help Show this help. + +The following commands are available: + next Show messages due in the next days. + check Check if a message is complete. + debug Print the effective configuration for a message, and the resulting + email message. + create Open a new message in an editor. + run Send out pending messages. +` // end::main[] + +var defaultConf = Configuration{ + Data: map[string]string{ + CONF_DAYS: "7", + CONF_SMTP_PORT: "587", + CONF_SMTP_SERVER: "localhost", + CONF_NOT_BEFORE: "00:00", + CONF_NOT_AFTER: "23:59", + }, +} + + +// Retrieve the user's home directory. +func userHome() string { + u, err := user.Current() + + // I don't think we can go on without finding the user's home directory + if err != nil { + panic(err) + } + + return u.HomeDir +} + +// Retrieve the user's config home, which is either defined through the environment variable +// `XDG_CONFIG_HOME` or is assumed to be a folder called `.config` in the user's home +// directory (as retrieved by `userHome()`). +func configHome() string { + value, present := os.LookupEnv("XDG_CONFIG_HOME") + + configHome := value + + if !present || value == "" || !filepath.IsAbs(value) { + configHome = filepath.Join(userHome(), ".config") + } + + return configHome +} + +// Expand `~/`, if the working directory begins with it. +func expandTilde(conf *Configuration) { + workdir := conf.Get(CONF_WORKDIR) + + if len(workdir) > 1 && workdir[0:2] == "~/" { + conf.Set(CONF_WORKDIR, filepath.Join(userHome(), workdir[2:])) + } else if len(workdir) == 1 && workdir == "~" { + conf.Set(CONF_WORKDIR, userHome()) + } +} + +// This will create all necessary folders in the working directory. +func createFolders(workdir string) error { + for _, dir := range []string{DIR_TODO, DIR_DONE, DIR_ERRORS, DIR_DRAFTS} { + err := os.MkdirAll(filepath.Join(workdir, dir), 0777) + + if err != nil { + return err + } + } + + return nil +} + +// Complain about files in DIR_ERRORS. +func alertIfErrors(workdir string) { + errorsDir := filepath.Join(workdir, DIR_ERRORS) + + count := 0 + + filepath.Walk(errorsDir, func(_ string, info os.FileInfo, _ error) error { + if info.Mode().IsRegular() { + count++ + } + + return nil + }) + + if count > 0 { + fmt.Println("There were failed messages.") + fmt.Printf("Please inspect the contents of the %s/ directory.\n", DIR_ERRORS) + } +} + +func main() { + userHome := userHome() + + // This is the configuration that will be cobbled together from the + // default configuration ('defaultConf'), the INI and the command line + // arguments. + sessionConf := NewConfiguration() + + sessionConf.MergeWith(&defaultConf) + + args, _ := docopt.Parse(usageMain, nil, true, "", true) + + // Determine configuration file name, defaults to `lettersnail.ini` in + // the user's config home (usually `.config`). + if args["--config"] != nil { + sessionConf.Set(CONF_CONFIG_FILENAME, args["--config"].(string)) + } else { + sessionConf.Set(CONF_CONFIG_FILENAME, filepath.Join(configHome(), "lettersnail.ini")) + } + + // Determine the desired command (run, next, ...) + command := args["<command>"].(string) + + // Load INI configuration. This will merge the '[default]' section with + // the optional section named after the desired command ('cmd'). + sessionConf.MergeWithIni(command) + + // Determine working directory, defaults to the folder `lettersnail` in + // the user's home directory. '--workdir' on the command line takes + // precedence over the 'workdir' in the INI. + if args["--workdir"] != nil { + sessionConf.Set(CONF_WORKDIR, args["--workdir"].(string)) + } else { + // If no working directory is set in the INI... + if sessionConf.Get(CONF_WORKDIR) == "" { + sessionConf.Set(CONF_WORKDIR, filepath.Join(userHome, "lettersnail")) + } + } + + // Expand the `~/` in workdir. + expandTilde(sessionConf) + + // Make sure that all necessary folders exist. + createFolders(sessionConf.Get(CONF_WORKDIR)) + + // See if there are files in DIR_ERRORS and alert the user. + alertIfErrors(sessionConf.Get(CONF_WORKDIR)) + + commandArgs := []string{command} + commandArgs = append(commandArgs, args["<args>"].([]string)...) + + switch command { + case "next": + cmd.Next(commandArgs, sessionConf) + case "create": + cmd.Create(commandArgs, sessionConf) + case "check": + cmd.Check(commandArgs, sessionConf) + case "run": + cmd.Run(commandArgs, sessionConf) + case "debug": + cmd.Debug(commandArgs, sessionConf) + } +}
\ No newline at end of file |