215 lines
5.5 KiB
Go
215 lines
5.5 KiB
Go
package projecttxt
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
type Project struct {
|
|
Id int `json:"id"` // Internal project id
|
|
Original string `json:"original"` // Original raw project text
|
|
Name string `json:"name"` // The name of the project
|
|
Directory string `json:"directory"` // The directory part of the project text
|
|
Notes string `json:"notes"` // The filename of the notes file, defaults to Directory+/+notes.md
|
|
Repo string `json:"repo"` // The directory to the git repository, defaults to Directory+/+Name
|
|
Contexts []string `json:"contexts"`
|
|
ProjectTags []string `json:"projectTags"`
|
|
AdditionalTags map[string]string `json:"additionalTags"`
|
|
}
|
|
|
|
// ParseProject parses the input text string into a Project struct
|
|
func ParseProject(text string) (*Project, error) {
|
|
var err error
|
|
project := Project{
|
|
AdditionalTags: make(map[string]string),
|
|
}
|
|
project.Original = strings.Trim(text, "\t\n\r ")
|
|
parts := getParts(project.Original)
|
|
//parts := strings.Fields(project.Original)
|
|
var pIdx int
|
|
for pIdx = range parts {
|
|
if len(parts[pIdx]) > 0 {
|
|
if parts[pIdx][0] == '^' {
|
|
break
|
|
}
|
|
project.Name = project.Name + " " + parts[pIdx]
|
|
}
|
|
}
|
|
project.Name = strings.TrimSpace(project.Name)
|
|
parts = parts[pIdx:]
|
|
var notesAbs, repoAbs bool
|
|
project.Notes = "notes.md"
|
|
project.Repo = project.Name
|
|
for _, v := range parts {
|
|
switch v[0] {
|
|
case '^': // Project Directory
|
|
project.Directory = v[1:]
|
|
if strings.HasSuffix(project.Directory, "/") {
|
|
project.Directory = strings.TrimSuffix(project.Directory, "/")
|
|
}
|
|
case '~': // Project Repo
|
|
project.Repo = v[1:]
|
|
if strings.HasPrefix(project.Repo, "/") {
|
|
repoAbs = true
|
|
}
|
|
case '#':
|
|
project.Notes = v[1:]
|
|
if strings.HasPrefix(project.Notes, "/") {
|
|
notesAbs = true
|
|
}
|
|
case '@': // Contexts
|
|
project.Contexts = append(project.Contexts, v[1:])
|
|
case '+': // ProjectTags
|
|
project.ProjectTags = append(project.ProjectTags, v[1:])
|
|
default:
|
|
if strings.Contains(v, ":") {
|
|
// Additional Tags
|
|
tagPts := strings.Split(v, ":")
|
|
if tagPts[0] != "" && tagPts[1] != "" {
|
|
project.AdditionalTags[tagPts[0]] = tagPts[1]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if !notesAbs {
|
|
project.Notes = fmt.Sprintf("%s/%s", project.Directory, project.Notes)
|
|
}
|
|
if !repoAbs {
|
|
project.Repo = fmt.Sprintf("%s/%s", project.Directory, project.Repo)
|
|
}
|
|
return &project, err
|
|
}
|
|
|
|
// getParts parses the text from text pulling out each part.
|
|
// By default it splits on spaces, the only exception is if we find a double-quote
|
|
func getParts(text string) []string {
|
|
var ret []string
|
|
var wrk string
|
|
quoteStart := -1
|
|
for i := range text {
|
|
if quoteStart >= 0 {
|
|
if text[i] == '"' {
|
|
quoteStart = -1
|
|
ret = append(ret, wrk)
|
|
} else {
|
|
wrk = wrk + string(text[i])
|
|
}
|
|
} else {
|
|
switch text[i] {
|
|
case '"':
|
|
quoteStart = i
|
|
case ' ':
|
|
if len(wrk) > 0 {
|
|
ret = append(ret, wrk)
|
|
}
|
|
wrk = ""
|
|
default:
|
|
wrk = wrk + string(text[i])
|
|
}
|
|
}
|
|
}
|
|
if len(wrk) > 0 {
|
|
ret = append(ret, wrk)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// String returns a complete project string in project.txt format.
|
|
//
|
|
// Contexts, ProjectTags, and Additional Tags are alphabetically sorted,
|
|
// and appended at the end in the following order:
|
|
// Contexts, ProjectTags, Tags
|
|
//
|
|
// For example:
|
|
// Project.txt ^/home/user/projects/projecttxt @personal +projectxt contact:"Frank Workman" "Address 1":"1234 Work Place"
|
|
func (project Project) String() string {
|
|
text := fmt.Sprintf("%s ", project.Name)
|
|
text += fmt.Sprintf("^%s ", project.Directory)
|
|
if len(project.Contexts) > 0 {
|
|
sort.Strings(project.Contexts)
|
|
for _, context := range project.Contexts {
|
|
text += fmt.Sprintf("@%s ", context)
|
|
}
|
|
}
|
|
if len(project.ProjectTags) > 0 {
|
|
sort.Strings(project.ProjectTags)
|
|
for _, pTag := range project.ProjectTags {
|
|
text += fmt.Sprintf("+%s ", pTag)
|
|
}
|
|
}
|
|
if len(project.AdditionalTags) > 0 {
|
|
keys := make([]string, 0, len(project.AdditionalTags))
|
|
for key := range project.AdditionalTags {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
for _, key := range keys {
|
|
text += fmt.Sprintf("%s:%s ", key, project.AdditionalTags[key])
|
|
}
|
|
}
|
|
return text
|
|
}
|
|
|
|
func (project *Project) HasContext(context string) bool {
|
|
for _, v := range project.Contexts {
|
|
if v == context {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (project *Project) GetContextsString() string {
|
|
var text string
|
|
sort.Strings(project.Contexts)
|
|
for _, context := range project.Contexts {
|
|
text += fmt.Sprintf("%s ", context)
|
|
}
|
|
return text
|
|
}
|
|
|
|
func (project *Project) HasProjectTag(projectTag string) bool {
|
|
for _, v := range project.ProjectTags {
|
|
if v == projectTag {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (project *Project) GetProjectsString() string {
|
|
var text string
|
|
sort.Strings(project.ProjectTags)
|
|
for _, pTag := range project.ProjectTags {
|
|
text += fmt.Sprintf("%s ", pTag)
|
|
}
|
|
return text
|
|
}
|
|
|
|
func (project *Project) SetTag(name, val string) {
|
|
project.AdditionalTags[name] = val
|
|
}
|
|
|
|
func (project *Project) HasTag(name string) bool {
|
|
_, ok := project.AdditionalTags[name]
|
|
return ok
|
|
}
|
|
|
|
func (project *Project) GetTag(name string) string {
|
|
return project.AdditionalTags[name]
|
|
}
|
|
|
|
func (project *Project) GetTagsString() string {
|
|
var text string
|
|
keys := make([]string, 0, len(project.AdditionalTags))
|
|
for key := range project.AdditionalTags {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
for _, key := range keys {
|
|
text += fmt.Sprintf("%s:%s ; ", key, project.AdditionalTags[key])
|
|
}
|
|
return text
|
|
}
|