go-projecttxt/projectlist.go

234 lines
6.7 KiB
Go

package projecttxt
import (
"bufio"
"errors"
"fmt"
"io/ioutil"
"os"
"strings"
)
type ProjectList struct {
Projects []*Project `json:"projects"`
}
func NewProjectList() *ProjectList { return &ProjectList{Projects: []*Project{}} }
func (projectlist *ProjectList) Size() int { return len(projectlist.Projects) }
func (projectlist *ProjectList) GetProjectSlice() []*Project { return projectlist.Projects }
func (projectlist *ProjectList) GetProjectsWithContext(context string) *ProjectList {
return projectlist.Filter(func(p *Project) bool {
return p.HasContext(context)
})
}
func (projectlist *ProjectList) GetProjectsWithProjectTag(projectTag string) *ProjectList {
return projectlist.Filter(func(p *Project) bool {
return p.HasProjectTag(projectTag)
})
}
// Filter filters the current TimerList for the given predicate (a function that takes a timer as input and returns a
// bool), and returns a new ProjectList. The original ProjectList is not modified.
func (projectlist *ProjectList) Filter(predicate func(*Project) bool) *ProjectList {
var newList ProjectList
for _, t := range projectlist.Projects {
if predicate(t) {
newList.AddProject(t)
}
}
return &newList
}
func (projectlist *ProjectList) GetNextId() int {
nextId := 0
for _, p := range projectlist.Projects {
if p.Id > nextId {
nextId = p.Id
}
}
return nextId + 1
}
// AddProject prepends a Project to the current ProjectList and takes care to set the ProjectId correctly
func (projectlist *ProjectList) AddProject(project *Project) {
project.Id = projectlist.GetNextId()
projectlist.Projects = append(projectlist.Projects, project)
}
// AddProjects adds all passed projects to the list
func (projectlist *ProjectList) AddProjects(projects []*Project) {
for _, v := range projects {
projectlist.AddProject(v)
}
}
func (projectlist *ProjectList) Combine(other *ProjectList) { projectlist.AddProjects(other.Projects) }
// GetProject returns the Project with the given project 'id' form the ProjectList.
// Returns an error if Project could not be found.
func (projectlist *ProjectList) GetProject(id int) (*Project, error) {
for i := range projectlist.Projects {
if projectlist.Projects[i].Id == id {
return projectlist.Projects[i], nil
}
}
return nil, errors.New("project not found")
}
// RemoveProjectById removes any Project with given Project 'id' from the ProjectList.
// Returns an error if no Project was removed.
func (projectlist *ProjectList) RemoveProjectById(id int) error {
found := false
var remIdx int
var p *Project
for remIdx, p = range projectlist.Projects {
if p.Id == id {
found = true
break
}
}
if !found {
return errors.New("project not found")
}
projectlist.Projects = append(projectlist.Projects[:remIdx], projectlist.Projects[remIdx+1:]...)
return nil
}
// RemoveProject removes any Project from the ProjectList with the same String representation as the given Project.
// Returns an error if no Project was removed.
func (projectlist *ProjectList) RemoveProject(project Project) error {
found := false
var remIdx int
var p *Project
for remIdx, p = range projectlist.Projects {
if p.String() == project.String() {
found = true
break
}
}
if !found {
return errors.New("project not found")
}
projectlist.Projects = append(projectlist.Projects[:remIdx], projectlist.Projects[remIdx+1:]...)
return nil
}
// ArchiveProjctToFile removes the project from the active list and concatenates it to
// the passed in filename
// Return an err if any part of that fails
func (projectlist *ProjectList) ArchiveProjectToFile(project Project, filename string) error {
if err := projectlist.RemoveProject(project); err != nil {
return err
}
f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
return err
}
defer f.Close()
_, err = f.WriteString(project.String() + "\n")
return err
}
func (projectlist *ProjectList) ResetIds() {
projectId := 1
for _, v := range projectlist.Projects {
v.Id = projectId
projectId++
}
}
// LoadFromFile loads a ProjectList from *os.File.
// Note: This will clear the current TimerList and overwrite it's contents with whatever is in *os.File.
func (projectlist *ProjectList) LoadFromFile(file *os.File) error {
projectlist.Projects = []*Project{} // Empty projectlist
projectId := 1
scanner := bufio.NewScanner(file)
for scanner.Scan() {
text := strings.Trim(scanner.Text(), "\t\n\r") // Read Line
// Ignore blank lines
if text == "" {
continue
}
project, err := ParseProject(text)
if err != nil {
return err
}
project.Id = projectId
projectId++
projectlist.Projects = append(projectlist.Projects, project)
}
if err := scanner.Err(); err != nil {
return err
}
return nil
}
// WriteToFile writes a ProjectList to *os.File
func (projectlist *ProjectList) WriteToFile(file *os.File) error {
writer := bufio.NewWriter(file)
_, err := writer.WriteString(projectlist.String())
writer.Flush()
if err != nil {
return err
}
projectlist.ResetIds()
return nil
}
// LoadFromFilename loads a ProjectList from the filename.
func (projectlist *ProjectList) LoadFromFilename(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
return projectlist.LoadFromFile(file)
}
// WriteToFilename writes a ProjectList to the specified file (most likely called "project.txt").
func (projectlist *ProjectList) WriteToFilename(filename string) error {
if err := ioutil.WriteFile(filename, []byte(projectlist.String()), 0640); err != nil {
return err
}
projectlist.ResetIds()
return nil
}
// String returns a complete list of projects in project.txt format.
func (projectlist ProjectList) String() string {
var ret string
for _, project := range projectlist.Projects {
ret += fmt.Sprintf("%s\n", project.String())
}
return ret
}
// LoadFromFile loads and returns a ProjectList from *os.File.
func LoadFromFile(file *os.File) (*ProjectList, error) {
projectlist := ProjectList{}
if err := projectlist.LoadFromFile(file); err != nil {
return nil, err
}
return &projectlist, nil
}
// WriteToFile writes a ProjectList to *os.File.
func WriteToFile(projectlist *ProjectList, file *os.File) error {
return projectlist.WriteToFile(file)
}
// LoadFromFilename loads and returns a ProjectList from a file (most likely called "project.txt")
func LoadFromFilename(filename string) (*ProjectList, error) {
projectlist := ProjectList{}
if err := projectlist.LoadFromFilename(filename); err != nil {
return nil, err
}
return &projectlist, nil
}
// WriteToFilename write a ProjectList to the specified file (most likely called "project.txt")
func WriteToFilename(projectlist *ProjectList, filename string) error {
return projectlist.WriteToFilename(filename)
}