Initial Commit
This commit is contained in:
commit
2fb4545a78
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Ignore the binary
|
||||
gask
|
123
app.go
Normal file
123
app.go
Normal 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
146
helpers.go
Normal 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
25
main.go
Normal 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
18
model_app.go
Normal 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
102
model_list.go
Normal 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
83
model_task.go
Normal 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(>.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
|
||||
}
|
Loading…
Reference in New Issue
Block a user