<commit_before>package cli

import (
	"time"

	"github.com/SeerUK/tid/pkg/state"
	"github.com/SeerUK/tid/pkg/tracking"
	"github.com/eidolon/console"
	"github.com/eidolon/console/parameters"
)

// EditCommand creates a command to edit timesheet entries.
func EditCommand(gateway tracking.Gateway) console.Command {
	var hash string
	var offset time.Duration
	var note string

	// We need an identifiable value for this
	duration := time.Duration(-1)

	configure := func(def *console.Definition) {
		def.AddOption(
			parameters.NewDurationValue(&duration),
			"-d, --duration=DURATION",
			"A new duration to set on the entry. Mutually exclusive with offset.",
		)

		def.AddOption(
			parameters.NewStringValue(&note),
			"-n, --note=NOTE",
			"A new note to set on the entry.",
		)

		def.AddOption(
			parameters.NewDurationValue(&offset),
			"-o, --offset=OFFSET",
			"A offset to modify the duration by (can be negative). Mutually excluse with duration.",
		)

		def.AddArgument(
			parameters.NewStringValue(&hash),
			"HASH",
			"A short or long hash for an entry.",
		)
	}

	execute := func(input *console.Input, output *console.Output) error {
		entry, err := gateway.FindEntry(hash)
		if err != nil && err != state.ErrNilResult {
			return err
		}

		if err == state.ErrNilResult {
			output.Printf("edit: No entry with hash '%s'\n", hash)
			return nil
		}

		status, err := gateway.FindOrCreateStatus()
		if err != nil {
			return err
		}

		// @todo: Maybe a facade?

		if status.IsActive() && status.Ref().Entry == entry.Hash() {
			output.Println("edit: Stop your existing timer before trying to edit it's entry")
			return nil
		}

		if duration >= 0 && offset != 0 {
			output.Println("edit: Cannot specify duration and offset at the same time")
			return nil
		}

		// Check for "zero-values"
		if duration < 0 {
			duration = entry.Duration()
		}

		if note == "" {
			note = entry.Note()
		}

		duration = duration + offset

		if duration < 0 {
			output.Println("edit: Duration can not be less than 0")
			return nil
		}

		entry.SetDuration(duration)
		entry.SetNote(note)

		err = gateway.PersistEntry(entry)
		if err != nil {
			return err
		}

		// @todo: Consider adding onSuccess / postExecute to eidolon/console.
		output.Printf("Updated entry '%s' (%s)\n", entry.Note(), entry.ShortHash())

		// @todo: Pretty table with old vs. new values.

		return nil
	}

	return console.Command{
		Name:        "edit",
		Description: "Edit a timesheet entry.",
		Configure:   configure,
		Execute:     execute,
	}
}
<commit_msg>Editing entries also needs to update the last updated time, so as not to throw off durations.<commit_after>package cli

import (
	"time"

	"github.com/SeerUK/tid/pkg/state"
	"github.com/SeerUK/tid/pkg/tracking"
	"github.com/eidolon/console"
	"github.com/eidolon/console/parameters"
)

// EditCommand creates a command to edit timesheet entries.
func EditCommand(gateway tracking.Gateway) console.Command {
	var hash string
	var offset time.Duration
	var note string

	// We need an identifiable value for this
	duration := time.Duration(-1)

	configure := func(def *console.Definition) {
		def.AddOption(
			parameters.NewDurationValue(&duration),
			"-d, --duration=DURATION",
			"A new duration to set on the entry. Mutually exclusive with offset.",
		)

		def.AddOption(
			parameters.NewStringValue(&note),
			"-n, --note=NOTE",
			"A new note to set on the entry.",
		)

		def.AddOption(
			parameters.NewDurationValue(&offset),
			"-o, --offset=OFFSET",
			"A offset to modify the duration by (can be negative). Mutually excluse with duration.",
		)

		def.AddArgument(
			parameters.NewStringValue(&hash),
			"HASH",
			"A short or long hash for an entry.",
		)
	}

	execute := func(input *console.Input, output *console.Output) error {
		entry, err := gateway.FindEntry(hash)
		if err != nil && err != state.ErrNilResult {
			return err
		}

		if err == state.ErrNilResult {
			output.Printf("edit: No entry with hash '%s'\n", hash)
			return nil
		}

		status, err := gateway.FindOrCreateStatus()
		if err != nil {
			return err
		}

		// @todo: Maybe a facade?

		if status.IsActive() && status.Ref().Entry == entry.Hash() {
			output.Println("edit: Stop your existing timer before trying to edit it's entry")
			return nil
		}

		if duration >= 0 && offset != 0 {
			output.Println("edit: Cannot specify duration and offset at the same time")
			return nil
		}

		// Check for "zero-values"
		if duration < 0 {
			duration = entry.Duration()
		}

		if note == "" {
			note = entry.Note()
		}

		duration = duration + offset

		if duration < 0 {
			output.Println("edit: Duration can not be less than 0")
			return nil
		}

		entry.SetDuration(duration)
		entry.SetNote(note)
		entry.Update()

		err = gateway.PersistEntry(entry)
		if err != nil {
			return err
		}

		// @todo: Consider adding onSuccess / postExecute to eidolon/console.
		output.Printf("Updated entry '%s' (%s)\n", entry.Note(), entry.ShortHash())

		// @todo: Pretty table with old vs. new values.

		return nil
	}

	return console.Command{
		Name:        "edit",
		Description: "Edit a timesheet entry.",
		Configure:   configure,
		Execute:     execute,
	}
}

{'fraction_non_alphanumeric': 0.12709739633558342, 'fraction_numerical': 0.0023143683702989393, 'mean_word_length': 3.1688102893890675, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2191211', 'n_tokens_mistral': 1911, 'n_tokens_neox': 1654, 'n_words': 509}