Initial Commit

This commit is contained in:
Brian Buller 2018-03-28 08:34:01 -05:00
commit 2fb4545a78
7 changed files with 499 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
# Ignore the binary
gask

123
app.go Normal file
View File

@ -0,0 +1,123 @@
package main
import (
"bufio"
"fmt"
"os"
"strings"
"git.bullercodeworks.com/brian/gask"
userConfig "github.com/br0xen/user-config"
)
// App holds everything we need to handle all application logic
type App struct {
Parms []Parameter
db *gask.GaskDB
cfg *userConfig.Config
}
// Parameter is a cli parameter and it's description
type Parameter struct {
Flag string
Desc []string
}
// NewApp creates the App object, which contains all application logic
func NewApp() *App {
var err error
// Create the App
app := App{}
// First we get the config
app.cfg, err = userConfig.NewConfig(AppName)
if err != nil {
fmt.Println("Creating new config")
app.cfg.Save()
}
wikifile := app.cfg.Get("wikifile")
// Try to open the db
if wikifile != "" {
// We've got a value, is it valid?
app.db, err = gask.NewGaskDB(wikifile)
for err != nil {
wikifile = ""
fmt.Println("Unable to open todos wiki file")
}
} else {
// No value for wikifile
wikifile = app.askForPath()
}
app.db, err = gask.NewGaskDB(wikifile)
if err != nil {
wikifile = ""
}
app.cfg.Set("wikifile", wikifile)
app.cfg.Save()
if wikifile == "" {
fmt.Println("Unable to open todos wiki file")
os.Exit(1)
}
err = app.CreateCliDb()
if err != nil {
fmt.Println("Unable to build CLI DB: " + err.Error())
os.Exit(1)
}
app.Parms = []Parameter{
Parameter{Flag: "add", Desc: []string{"Add a task to an existing list"}},
Parameter{Flag: "addlist", Desc: []string{"Add a list to the database"}},
Parameter{Flag: "done", Desc: []string{"Mark a task done"}},
Parameter{Flag: "help", Desc: []string{"Print this message"}},
Parameter{Flag: "lists", Desc: []string{"Print Lists"}},
Parameter{Flag: "ls", Desc: []string{"Print Tasks"}},
}
return &app
}
func (a *App) Execute(args []string) error {
if len(args) == 0 {
args = append(args, "ls")
}
var opts []string
for i := range a.Parms {
opts = append(opts, a.Parms[i].Flag)
}
fnd := matchString(args[0], opts)
switch fnd {
// Task cases
case "add":
return a.AddTask(args[1:])
case "done":
return a.CompleteTask(args[1:])
case "ls": // List tasks
return a.PrintTasks(args[1:])
// List cases
case "lists": // List lists
return a.PrintLists(args[1:])
case "addlist":
return a.AddList(args[1:])
case "help":
return a.PrintHelp(args[1:])
}
return nil
}
func (a *App) PrintHelp(args []string) error {
return nil
}
// askForPath asks the user to enter the path to their todo wiki file
func (a *App) askForPath() string {
fmt.Println("Please specify the full path to the wiki file that contains your todo lists:")
fmt.Println("(Leave blank to exit)")
fmt.Print("> ")
reader := bufio.NewReader(os.Stdin)
wikifile, _ := reader.ReadString('\n')
wikifile = strings.TrimSpace(wikifile)
return wikifile
}

146
helpers.go Normal file
View File

@ -0,0 +1,146 @@
package main
import (
"errors"
"math"
"strings"
"git.bullercodeworks.com/brian/gask"
)
// matchString gets the closest match of 'ndl' in 'hay'
func matchString(ndl string, hay []string) string {
var nextParms []string
for i := range ndl {
for _, p := range hay {
if p[i] == ndl[i] {
nextParms = append(nextParms, p)
}
}
// If we get here and there is only one string left, return it
hay = nextParms
if len(nextParms) == 1 {
break
}
// Otherwise, loop
nextParms = []string{}
}
if len(hay) == 0 {
return ""
}
return hay[0]
}
// matchStrings returns _all_ strings in hay that match ndl
func matchStrings(ndl string, hay []string) []string {
var nextParms []string
for i := range ndl {
for _, p := range hay {
if p[i] == ndl[i] {
nextParms = append(nextParms, p)
}
}
// If we get here and there is only one string left, return it
hay = nextParms
if len(nextParms) == 1 {
break
}
// Otherwise, loop
nextParms = []string{}
}
if len(hay) == 0 {
return []string{}
}
return hay
}
func indexToAlpha(id int) string {
// We want 0 to be 'a'
id = id + 1
var ret string
d, r := id/26, id%26
if r == 0 {
d, r = d-1, 26
}
if d > 26 {
return indexToAlpha(d) + string('a'+r-1)
}
if d > 0 {
ret = string('a' + d - 1)
}
return ret + string('a'+r-1)
}
func alphaToIndex(id string) int {
var j, ret int
for i := len(id) - 1; i >= 0; i-- {
ret += int(id[i]-'a'+1) * int(math.Pow(26, float64(j)))
j++
}
ret--
return ret
}
// taskToAppString takes a task ID and a the task and returns the string ready to be output
func taskToAppString(stId string, gt *gask.GaskTask) string {
var ret string
pct := gt.GetPctComplete()
str := strings.Repeat(" ", gt.Depth) // Task depth
str += stId // Task ID
if pct == 1 { // % Complete
str += "[X]"
} else if pct > 0.67 {
ret += "[O]"
} else if pct > 0.34 {
ret += "[o]"
} else if pct > 0 {
ret += "[.]"
} else {
ret += "[ ]"
}
ret += gt.Description // Description
//tskCnt++
//for _, st := range gt.Subtasks {
//}
return ret
}
// getListTaskId returns the task id for the given task in the given list
func getListTaskId(lst *gask.GaskList, tsk *gask.GaskTask) (int, error) {
var tskCnt int
for i := 0; i < lst.Tasks.Length(); i++ {
lt := lst.Tasks.Get(i)
if lt == tsk {
return tskCnt, nil
}
tskCnt++
if lt.GetSubtaskCount() > 0 {
if stCnt, err := getSubtaskId(lt, tsk); err != nil {
return (tskCnt + stCnt), nil
}
tskCnt += lt.GetSubtaskCount() - 1
}
}
return -1, errors.New("Couldn't find task")
}
func getSubtaskId(tsk *gask.GaskTask, sub *gask.GaskTask) (int, error) {
var tskCnt int
for _, st := range tsk.Subtasks {
if &st == sub {
return tskCnt, nil
}
tskCnt++
if st.GetSubtaskCount() > 0 {
if stCnt, err := getSubtaskId(&st, sub); err != nil {
return (tskCnt + stCnt), nil
}
tskCnt += st.GetSubtaskCount() - 1
}
}
return -1, errors.New("Couldn't find subtask")
}

25
main.go Normal file
View File

@ -0,0 +1,25 @@
package main
import (
"fmt"
"os"
)
const AppName = "gask"
var app *App
func init() {
app = NewApp()
}
func main() {
if app.db == nil {
os.Exit(1)
}
var err error
if err = app.Execute(os.Args[1:]); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

18
model_app.go Normal file
View File

@ -0,0 +1,18 @@
package main
import "errors"
type CliDB struct {
Lists []CliList
}
// CreateCliDb builds the CLI App's DB
func (a *App) CreateCliDb() error {
if app.db == nil {
return errors.New("No GaskDB loaded")
}
//var cliDb *CliDB
return nil
}

102
model_list.go Normal file
View File

@ -0,0 +1,102 @@
package main
import (
"errors"
"fmt"
"unicode"
"git.bullercodeworks.com/brian/gask"
)
type CliList struct {
Id string // The CLI alpha-id of this list
Tasks []CliTask // The lists tasks
dbList *gask.GaskList // The DB Backing for this list
}
func NewCliList(gl *gask.GaskList) *CliList {
ret := new(CliList)
for i := 0; i < gl.Tasks.Length(); i++ {
ret.Tasks = append(ret.Tasks, *NewCliTask(gl.Tasks.Get(i)))
}
ret.dbList = gl
return ret
}
func (cl *CliList) SetId(id string) {
cl.Id = id
}
// GetList takes the id argument and returns the list for the id
// e.g. 'b10' would return list 'b'
func (a *App) GetList(id string) (*gask.GaskList, error) {
var lId string
for _, r := range id {
if !unicode.IsLetter(r) {
break
}
lId = lId + string(r)
}
// lId should now be the alpha-id of the list
if len(lId) == 0 {
return nil, errors.New("Invalid list id given: " + id)
}
idx := alphaToIndex(id)
if a.db.Lists.Length() < idx {
return nil, errors.New("Invalid list id given: " + id)
}
return a.db.Lists.Get(idx), nil
}
// AddList adds a new list
func (a *App) AddList(args []string) error {
//a.db.AddList
return nil
}
// PrintLists prints all list names with their alpha-ids
func (a *App) PrintLists(args []string) error {
if a.db.Lists.Length() == 0 {
return errors.New("No lists found")
}
matchedLists := a.db.Lists
if len(args) > 0 {
// Try to match the first argument to a list name
var allLists []string
for i := 0; i < a.db.Lists.Length(); i++ {
allLists = append(allLists, a.db.Lists.Get(i).Name)
}
mtch := matchStrings(args[0], allLists)
if len(mtch) > 0 {
// We matched a list, report the id and name
} else {
}
}
if matchedLists.Length() == 0 {
return errors.New("No lists matched the search criteria")
}
for i := 0; i < matchedLists.Length(); i++ {
fmt.Println("(" + indexToAlpha(i+1) + ") " + a.db.Lists.Get(i).Name)
}
return nil
}
// PrintList prints the lId-th list in the Todos file
func (a *App) PrintList(lId string) error {
listId := 0
for i := range lId {
listId += int(lId[i]) - int('a')
}
if listId < 0 || listId >= a.db.Lists.Length() {
return errors.New("Invalid list id")
}
lst := a.db.Lists.Get(listId)
fmt.Println(lId, lst.Name)
for i := 0; i < lst.Tasks.Length(); i++ {
a.PrintTask(lId, i)
}
return nil
}

83
model_task.go Normal file
View File

@ -0,0 +1,83 @@
package main
import (
"errors"
"fmt"
"strconv"
"git.bullercodeworks.com/brian/gask"
)
type CliTask struct {
Id string // This tasks full id (alpha-numeric)
ParentId string
Description string
IsDone bool
Depth int
Subtasks []CliTask // The Task's subtasks
dbTask *gask.GaskTask // The DB Backing for this task
}
func NewCliTask(gt *gask.GaskTask) *CliTask {
ret := new(CliTask)
ret.Description = gt.Description
ret.IsDone = gt.IsDone
ret.Depth = gt.Depth
for i := 0; i < len(gt.Subtasks); i++ {
ret.Subtasks = append(ret.Subtasks, *NewCliTask(&gt.Subtasks[i]))
}
ret.dbTask = gt
return ret
}
// GetTask takes the id argument and returns the task for the id
// e.g. 'b10' would return the 11th (0-indexed) task from list 'b'
func (a *App) GetTask(id string) (*gask.GaskTask, error) {
var err error
var lst *gask.GaskList
if lst, err = a.GetList(id); err != nil {
return nil, err
}
var ret *gask.GaskTask
_ = lst
return ret, errors.New("Couldn't find task")
}
// AddTask adds a task
func (a *App) AddTask(args []string) error {
return nil
}
// CompleteTasks marks a task as complete
func (a *App) CompleteTask(args []string) error {
return nil
}
// PrintTasks lists tasks in the db
func (a *App) PrintTasks(args []string) error {
var err error
if len(args) > 0 {
if err = a.PrintList(args[0]); err != nil {
return err
}
} else {
// Just list everything
for lstId := 0; lstId < a.db.Lists.Length(); lstId++ {
var tskCnt int
lstAlphaId := indexToAlpha(lstId)
fmt.Println("@" + lstAlphaId + " " + a.db.Lists.Get(lstId).Name)
for i := 0; i < a.db.Lists.Get(lstId).Tasks.Length(); i++ {
tskId := "@" + lstAlphaId + strconv.Itoa(tskCnt)
fmt.Println(taskToAppString(tskId, a.db.Lists.Get(lstId).Tasks.Get(i)))
}
}
}
return nil
}
// printTask prints the tId-th task in list lId with all of it's detail
func (a *App) PrintTask(lId string, tId int) error {
return nil
}