package cmd import ( "encoding/json" "fmt" "regexp" "strings" "github.com/lerko/nib/internal/db" "github.com/lerko/nib/internal/display" "github.com/spf13/cobra" ) var promoteCmd = &cobra.Command{ Use: "promote [type]", Short: "promote a fluid entity to a card", Args: cobra.RangeArgs(1, 2), RunE: runPromote, } func init() { rootCmd.AddCommand(promoteCmd) } func runPromote(_ *cobra.Command, args []string) error { store, err := openStore() if err != nil { return err } defer store.Close() id, err := store.Resolve(args[0]) if err != nil { return fmt.Errorf("not_found — no entity with id %s", args[0]) } cardType := db.CardSnippet if len(args) > 1 { if !db.ValidCardType(args[1]) { return fmt.Errorf("invalid_type — %q is not a valid card type", args[1]) } cardType = db.CardType(args[1]) } e, err := store.Get(id) if err != nil { return err } cardData := generateCardData(cardType, e.Body) if err := store.Promote(id, cardType, cardData); err != nil { if err == db.ErrAlreadyPromoted { return fmt.Errorf("invalid_promote — entity %s is already a %s", display.FormatID(id), *e.CardType) } return err } fmt.Printf("promoted %s → %s\n", display.FormatID(id), cardType) return nil } var templateSlotRe = regexp.MustCompile(`\$\{(\w+)\}`) func generateCardData(ct db.CardType, body string) *string { var data string switch ct { case db.CardTemplate: matches := templateSlotRe.FindAllStringSubmatch(body, -1) type slot struct { Name string `json:"name"` Default string `json:"default"` } var slots []slot seen := map[string]bool{} for _, m := range matches { name := m[1] if !seen[name] { slots = append(slots, slot{Name: name, Default: ""}) seen[name] = true } } if slots == nil { slots = []slot{} } b, _ := json.Marshal(map[string]any{"slots": slots}) data = string(b) case db.CardChecklist: type step struct { Text string `json:"text"` Done bool `json:"done"` } var steps []step for _, line := range strings.Split(body, "\n") { line = strings.TrimSpace(line) if strings.HasPrefix(line, "[ ]") || strings.HasPrefix(line, "[x]") { text := strings.TrimSpace(line[3:]) done := strings.HasPrefix(line, "[x]") steps = append(steps, step{Text: text, Done: done}) } } if steps == nil { steps = []step{{Text: body, Done: false}} } b, _ := json.Marshal(map[string]any{"steps": steps}) data = string(b) case db.CardDecision: b, _ := json.Marshal(map[string]any{ "chose": "", "why": "", "rejected": []string{}, }) data = string(b) case db.CardLink: url := "" for _, word := range strings.Fields(body) { if strings.HasPrefix(word, "http://") || strings.HasPrefix(word, "https://") { url = word break } } b, _ := json.Marshal(map[string]any{"url": url}) data = string(b) default: data = "{}" } return &data }