package cmd import ( "encoding/json" "fmt" "os" "github.com/lerko/nib/internal/db" "github.com/lerko/nib/internal/export" "github.com/spf13/cobra" ) var ( exportOutput string exportFormat string exportTag string exportTitle string ) var exportCmd = &cobra.Command{ Use: "export", Short: "export entities to JSON or HTML card deck", RunE: runExport, } func init() { exportCmd.Flags().StringVarP(&exportOutput, "output", "o", "", "write to file instead of stdout") exportCmd.Flags().StringVarP(&exportFormat, "format", "f", "json", "output format: json or html") exportCmd.Flags().StringVarP(&exportTag, "tag", "t", "", "filter by tag (used as deck name for HTML)") exportCmd.Flags().StringVar(&exportTitle, "title", "", "deck title for HTML export") rootCmd.AddCommand(exportCmd) } type exportEntity struct { ID string `json:"id"` CreatedAt string `json:"created_at"` ModifiedAt string `json:"modified_at"` Body string `json:"body"` Title *string `json:"title,omitempty"` Description *string `json:"description,omitempty"` Glyph string `json:"glyph"` TimeAnchor *string `json:"time_anchor,omitempty"` CompletedAt *string `json:"completed_at,omitempty"` Pinned bool `json:"pinned"` DeletedAt *string `json:"deleted_at,omitempty"` Tags []string `json:"tags"` CardType *string `json:"card_type,omitempty"` CardData *string `json:"card_data,omitempty"` UseCount int `json:"use_count"` LastUsedAt *string `json:"last_used_at,omitempty"` } func runExport(cmd *cobra.Command, _ []string) error { store, err := openStore() if err != nil { return err } defer store.Close() p := db.DefaultListParams() p.Limit = 10000 if exportTag != "" { p.Tag = &exportTag } switch exportFormat { case "html": return runHTMLExport(cmd, store, p) case "json": p.IncludeDeleted = true return runJSONExport(cmd, store, p) default: return fmt.Errorf("unknown format %q (use json or html)", exportFormat) } } func runHTMLExport(cmd *cobra.Command, store *db.Store, p db.ListParams) error { p.CardsOnly = true entities, err := store.List(cmd.Context(), p) if err != nil { return err } title := exportTitle if title == "" && exportTag != "" { title = "#" + exportTag } if title == "" { title = "nib cards" } if exportOutput != "" { f, err := os.Create(exportOutput) if err != nil { return err } defer f.Close() if err := export.RenderHTML(f, entities, title); err != nil { return err } fmt.Fprintf(cmd.ErrOrStderr(), "exported %d cards to %s\n", len(entities), exportOutput) return nil } return export.RenderHTML(cmd.OutOrStdout(), entities, title) } func runJSONExport(cmd *cobra.Command, store *db.Store, p db.ListParams) error { entities, err := store.List(cmd.Context(), p) if err != nil { return err } out := make([]exportEntity, len(entities)) for i, e := range entities { out[i] = exportEntity{ ID: e.ID, CreatedAt: e.CreatedAt.Format("2006-01-02T15:04:05Z07:00"), ModifiedAt: e.ModifiedAt.Format("2006-01-02T15:04:05Z07:00"), Body: e.Body, Title: e.Title, Glyph: string(e.Glyph), TimeAnchor: e.TimeAnchor, Pinned: e.Pinned, Tags: e.Tags, CardData: e.CardData, UseCount: e.UseCount, } if e.Description != nil { out[i].Description = e.Description } if e.CompletedAt != nil { s := e.CompletedAt.Format("2006-01-02T15:04:05Z07:00") out[i].CompletedAt = &s } if e.DeletedAt != nil { s := e.DeletedAt.Format("2006-01-02T15:04:05Z07:00") out[i].DeletedAt = &s } if e.CardType != nil { s := string(*e.CardType) out[i].CardType = &s } if e.LastUsedAt != nil { s := e.LastUsedAt.Format("2006-01-02T15:04:05Z07:00") out[i].LastUsedAt = &s } } data, err := json.MarshalIndent(out, "", " ") if err != nil { return err } if exportOutput != "" { if err := os.WriteFile(exportOutput, data, 0o600); err != nil { return err } fmt.Fprintf(cmd.ErrOrStderr(), "exported %d entities to %s\n", len(out), exportOutput) return nil } fmt.Println(string(data)) return nil }