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) }