command.go (9424B)
1 package main 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "strings" 8 "time" 9 ) 10 11 type Command struct { 12 repository FSRepository 13 args []string 14 timeContext *TimeContext 15 } 16 17 func (c *Command) nextArg() (string, error) { 18 if len(c.args) == 0 { 19 return "", errors.New("No more arguments") 20 } 21 22 if len(c.args) == 1 { 23 first := (c.args)[0] 24 c.args = make([]string, 0) 25 return first, nil 26 } 27 28 first := (c.args)[0] 29 c.args = (c.args)[1:] 30 return first, nil 31 } 32 33 func (c Command) Add() { 34 if len(c.args) != 3 { 35 fmt.Fprintln(os.Stderr, "add <group> <start> <stop>") 36 os.Exit(1) 37 } 38 39 groupArg, _ := c.nextArg() 40 startArg, _ := c.nextArg() 41 stopArg, _ := c.nextArg() 42 43 start, err := c.timeContext.ParseArgDir(startArg, false) 44 if err != nil { 45 fmt.Fprintf(os.Stderr, "[start] time (%s) could not be parsed\n", startArg) 46 os.Exit(1) 47 } 48 49 stop, err := tryParseRelative(*c.timeContext, start, stopArg) 50 if err != nil { 51 fmt.Fprintf(os.Stderr, "[stop] time (%s) could not be parsed\n", stopArg) 52 os.Exit(1) 53 } 54 55 group := NewGroup(groupArg) 56 57 entry, err := NewCompletedEntry(start, stop) 58 if err != nil { 59 fmt.Fprint(os.Stderr, err) 60 os.Exit(1) 61 } 62 63 if c.repository.ExistsEntry(group, entry) { 64 fmt.Fprintln(os.Stderr, "entry already created") 65 os.Exit(1) 66 } 67 68 c.repository.Save(group, entry) 69 } 70 71 func (c Command) Start() { 72 if len(c.args) < 1 || len(c.args) > 2 { 73 fmt.Fprintln(os.Stderr, "start <group> [<start>]") 74 os.Exit(1) 75 } 76 77 groupArg, _ := c.nextArg() 78 group := NewGroup(groupArg) 79 80 startArg, err := c.nextArg() 81 start := time.Now() 82 if err == nil { 83 start, err = c.timeContext.ParseArgDir(startArg, false) 84 if err != nil { 85 fmt.Fprintf(os.Stderr, "[start] time (%s) could not be parsed\n", startArg) 86 os.Exit(1) 87 } 88 } 89 90 entry := NewRunningEntry(start) 91 92 if c.repository.ExistsEntry(group, entry) { 93 fmt.Fprintln(os.Stderr, "entry in this group already started") 94 os.Exit(1) 95 } 96 97 c.repository.Save(group, entry) 98 } 99 100 func (c Command) Stop() { 101 if len(c.args) < 1 || len(c.args) > 2 { 102 fmt.Fprintln(os.Stderr, "stop <group> [<stop>]") 103 os.Exit(1) 104 } 105 106 groupArg, _ := c.nextArg() 107 group := NewGroup(groupArg) 108 109 runningEntry, err := c.repository.RunningEntry(group, c.timeContext) 110 if err != nil { 111 fmt.Fprintf(os.Stderr, "no entry running in %q\n", groupArg) 112 os.Exit(1) 113 } 114 115 stop := time.Now() 116 stopArg, err := c.nextArg() 117 if err == nil { 118 stop, err = tryParseRelative(*c.timeContext, runningEntry.TimeRange.Start, stopArg) 119 if err != nil { 120 fmt.Fprintf(os.Stderr, "[start] time (%s) could not be parsed\n", stopArg) 121 os.Exit(1) 122 } 123 } 124 125 completedEntry, err := NewCompletedEntry(runningEntry.TimeRange.Start, stop) 126 if err != nil { 127 fmt.Fprintln(os.Stderr, err) 128 os.Exit(1) 129 } 130 131 if c.repository.ExistsEntry(group, completedEntry) { 132 fmt.Fprintln(os.Stderr, "entry already created") 133 os.Exit(1) 134 } 135 136 err = c.repository.Save(group, completedEntry) 137 if err != nil { 138 fmt.Fprintln(os.Stderr, err) 139 os.Exit(1) 140 } 141 142 c.repository.Remove(group, runningEntry) 143 } 144 145 func (c Command) Ls() { 146 if len(c.args) > 1 { 147 fmt.Fprintln(os.Stderr, "ls [<group>]") 148 os.Exit(1) 149 } 150 151 rootPath := "" 152 if len(c.args) == 1 { 153 rootPath, _ = c.nextArg() 154 } 155 156 group := NewGroup(rootPath) 157 formatEntries(c.repository, group, c.timeContext) 158 } 159 160 func (c Command) Lsr() { 161 if len(c.args) > 1 { 162 fmt.Fprintln(os.Stderr, "lsr [<group>]") 163 os.Exit(1) 164 } 165 166 groupPath := "" 167 if len(c.args) == 1 { 168 groupPath, _ = c.nextArg() 169 } 170 171 groups, err := c.repository.ListGroups(groupPath) 172 if err != nil { 173 fmt.Fprintln(os.Stderr, err) 174 os.Exit(1) 175 } 176 177 for _, group := range groups { 178 formatEntries(c.repository, group, c.timeContext) 179 } 180 } 181 182 func (c Command) Report() { 183 if len(c.args) < 1 || len(c.args) > 2 { 184 fmt.Fprintln(os.Stderr, "report <since> [<until>]") 185 os.Exit(1) 186 } 187 188 // TODO(tms) 11.02.23: make as parameter --group 189 groupPath := "" 190 // if len(c.args) == 2 { 191 // groupPath, _ = c.nextArg() 192 // } 193 194 sinceArg, _ := c.nextArg() 195 sinceTime, err := c.timeContext.ParseArgDir(sinceArg, false) 196 if err != nil { 197 fmt.Fprintf(os.Stderr, "[since] time (%s) could not be parsed\n", sinceArg) 198 os.Exit(1) 199 } 200 201 untilTime := time.Now() 202 untilArgs, err := c.nextArg() 203 if err == nil { 204 untilTime, err = c.timeContext.ParseArgDir(untilArgs, true) 205 if err != nil { 206 fmt.Fprintf(os.Stderr, "[until] time (%s) could not be parsed\n", sinceArg) 207 os.Exit(1) 208 } 209 } 210 211 groups, err := c.repository.ListGroups(groupPath) 212 if err != nil { 213 fmt.Fprintln(os.Stderr, err) 214 os.Exit(1) 215 } 216 217 var atLeastOneEntry bool 218 var totalDuration time.Duration 219 for _, group := range groups { 220 entries, err := c.repository.ListEntries(group, c.timeContext) 221 if err != nil { 222 fmt.Fprintln(os.Stderr, err) 223 os.Exit(1) 224 } 225 226 for _, entry := range entries { 227 if !entry.Completed { 228 continue 229 } 230 231 if (entry.TimeRange.Start.After(sinceTime) || entry.TimeRange.Start.Equal(sinceTime)) && 232 (entry.TimeRange.Stop.Before(untilTime) || entry.TimeRange.Stop.Equal(untilTime)) { 233 atLeastOneEntry = true 234 totalDuration += entry.Duration() 235 formatEntry(group, entry) 236 } 237 } 238 } 239 240 if atLeastOneEntry { 241 fmt.Printf("%s\n", totalDuration.Round(time.Second)) 242 } 243 } 244 245 func formatEntries(repository FSRepository, group Group, entryTime *TimeContext) { 246 entries, err := repository.ListEntries(group, entryTime) 247 if err != nil { 248 fmt.Fprintln(os.Stderr, err) 249 os.Exit(1) 250 } 251 252 for _, entry := range entries { 253 formatEntry(group, entry) 254 } 255 } 256 257 func sFormatEntry(group Group, entry Entry) string { 258 groupPath := group.Name 259 timeLayout := "15:04:05 02/01/2006" 260 261 var start, stop string 262 var duration time.Duration 263 start = entry.TimeRange.Start.Format(timeLayout) 264 if entry.Completed { 265 stop = entry.TimeRange.Stop.Format(timeLayout) 266 duration = entry.Duration().Round(time.Second) 267 } else { 268 start = entry.TimeRange.Start.Format(timeLayout) 269 stop = "running" 270 duration = time.Since(entry.TimeRange.Start).Round(time.Second) 271 } 272 273 return fmt.Sprintf("%s\t%s\t%v\t%s", groupPath, start, stop, duration) 274 } 275 276 func formatEntry(group Group, entry Entry) { 277 fmt.Println(sFormatEntry(group, entry)) 278 } 279 280 func tryParseRelative(timeContext TimeContext, start time.Time, rawStop string) (stop time.Time, err error) { 281 if strings.IndexAny(rawStop, "+") == 0 { 282 stop, err = timeContext.ParseArgRelative(start, rawStop) 283 } else { 284 stop, err = timeContext.ParseArgDir(rawStop, false) 285 } 286 return stop, err 287 } 288 289 func (c Command) Status() { 290 groups, err := c.repository.ListGroups("") 291 if err != nil { 292 fmt.Fprintln(os.Stderr, err) 293 os.Exit(1) 294 } 295 296 for _, group := range groups { 297 entries, err := c.repository.ListEntries(group, c.timeContext) 298 if err != nil { 299 fmt.Fprintln(os.Stderr, err) 300 os.Exit(1) 301 } 302 303 for _, entry := range entries { 304 if entry.Completed { 305 continue 306 } 307 308 formatEntry(group, entry) 309 } 310 } 311 } 312 313 func (c Command) selectEntry(groupPath string) (Group, Entry, error) { 314 groups, err := c.repository.ListGroups(groupPath) 315 if err != nil { 316 return Group{}, Entry{}, err 317 } 318 319 type groupEntry struct { 320 group Group 321 entry Entry 322 } 323 324 gEntries := make(map[int]groupEntry) 325 326 // TODO: most fresh entry be 1 327 counter := 1 328 for _, group := range groups { 329 entries, err := c.repository.ListEntries(group, c.timeContext) 330 if err != nil { 331 return Group{}, Entry{}, err 332 } 333 334 for _, entry := range entries { 335 gEntries[counter] = groupEntry{ 336 group, 337 entry, 338 } 339 fmt.Printf("%d. %s\n", counter, sFormatEntry(group, entry)) 340 counter = counter + 1 341 } 342 } 343 344 // TODO: format be 1,2,3 ; 1-3 ; 1,3,5-10 ; 1: (1 to end) ; :10 (start to ten) ; 345 var number int 346 _, err = fmt.Scanln(&number) 347 if err != nil { 348 return Group{}, Entry{}, err 349 } 350 351 ge := gEntries[number] 352 return ge.group, ge.entry, nil 353 } 354 355 func (c Command) Remove() { 356 if len(c.args) != 1 { 357 fmt.Fprintln(os.Stderr, "rm <group>") 358 os.Exit(1) 359 } 360 361 groupArg, _ := c.nextArg() 362 if group := NewGroup(groupArg); !c.repository.Exists(group) { 363 fmt.Fprintln(os.Stderr, "group does not exists") 364 os.Exit(1) 365 } 366 367 group, entry, err := c.selectEntry(groupArg) 368 if err != nil { 369 fmt.Fprintln(os.Stderr, err) 370 os.Exit(1) 371 } 372 373 fmt.Printf("\n%s\n\nConfirm removal (y/n):", sFormatEntry(group, entry)) 374 375 var answer string 376 _, err = fmt.Scanln(&answer) 377 if err != nil { 378 fmt.Println(err) 379 os.Exit(1) 380 } 381 382 if strings.ToLower(answer) == "y" { 383 err = c.repository.Remove(group, entry) 384 if err != nil { 385 fmt.Print(err) 386 os.Exit(1) 387 } 388 } 389 390 } 391 392 func (c Command) Move() { 393 if len(c.args) != 2 { 394 fmt.Fprintln(os.Stderr, "mv <group-from> <group-to>") 395 os.Exit(1) 396 } 397 398 groupFromArg, _ := c.nextArg() 399 groupFrom := NewGroup(groupFromArg) 400 if !c.repository.Exists(groupFrom) { 401 fmt.Fprintf(os.Stderr, "group-from %q does not exists\n", groupFrom.Name) 402 os.Exit(1) 403 } 404 405 groupToArg, _ := c.nextArg() 406 groupTo := NewGroup(groupToArg) 407 if !c.repository.Exists(groupTo) { 408 fmt.Fprintf(os.Stderr, "group-to %q does not exists\n", groupTo.Name) 409 os.Exit(1) 410 } 411 412 group, entry, err := c.selectEntry(groupFromArg) 413 if err != nil { 414 fmt.Fprintln(os.Stderr, err) 415 os.Exit(1) 416 } 417 418 // TODO(tms) 14.04.23: format: <from> -> <to> [overwrite] 419 fmt.Printf("\n%s\n\nConfirm move (y/n):", sFormatEntry(group, entry)) 420 421 var answer string 422 _, err = fmt.Scanln(&answer) 423 if err != nil { 424 fmt.Fprintln(os.Stderr, err) 425 os.Exit(1) 426 } 427 428 if strings.ToLower(answer) == "y" { 429 _, err = c.repository.Move(groupFrom, []Entry{entry}, groupTo) 430 if err != nil { 431 fmt.Fprintln(os.Stderr, err) 432 os.Exit(1) 433 } 434 } 435 }