package commands import ( "database/sql" "fmt" "regexp" "strings" "punchcard/internal/context" "punchcard/internal/queries" "github.com/spf13/cobra" ) func NewAddClientCmd() *cobra.Command { cmd := &cobra.Command{ Use: "client []", Short: "Add a new client", Long: "Add a new client to the database. Name can include email in format 'Name '", Args: cobra.RangeArgs(1, 2), RunE: func(cmd *cobra.Command, args []string) error { name, email := parseNameAndEmail(args) billableRateFloat, _ := cmd.Flags().GetFloat64("hourly-rate") billableRate := int64(billableRateFloat * 100) q := context.GetDB(cmd.Context()) if q == nil { return fmt.Errorf("database not available in context") } var emailParam sql.NullString if email != "" { emailParam = sql.NullString{String: email, Valid: true} } var billableRateParam sql.NullInt64 if billableRate > 0 { billableRateParam = sql.NullInt64{Int64: billableRate, Valid: true} } client, err := q.CreateClient(cmd.Context(), queries.CreateClientParams{ Name: name, Email: emailParam, BillableRate: billableRateParam, }) if err != nil { return fmt.Errorf("failed to create client: %w", err) } output := fmt.Sprintf("Created client: %s", client.Name) if client.Email.Valid { output += fmt.Sprintf(" <%s>", client.Email.String) } output += fmt.Sprintf(" (ID: %d)\n", client.ID) cmd.Print(output) return nil }, } cmd.Flags().Float64P("hourly-rate", "r", 0, "Default hourly billable rate for this client") return cmd } func parseNameAndEmail(args []string) (string, string) { nameArg := args[0] var emailArg string if len(args) > 1 { emailArg = args[1] } if emailArg != "" { if matches := emailAndNameRegex.FindStringSubmatch(emailArg); matches != nil { emailArg = strings.TrimSpace(matches[2]) } } if matches := emailAndNameRegex.FindStringSubmatch(nameArg); matches != nil { nameArg = strings.TrimSpace(matches[1]) if emailArg == "" { emailArg = strings.TrimSpace(matches[2]) } } return nameArg, emailArg } var emailAndNameRegex = regexp.MustCompile(`^(.+?)<([^>]+@[^>]+)>$`)