gogcli/internal/cmd/docs.go
2025-12-26 22:24:22 +01:00

191 lines
4.5 KiB
Go

package cmd
import (
"errors"
"fmt"
"io"
"os"
"strings"
"github.com/spf13/cobra"
"github.com/steipete/gogcli/internal/outfmt"
"github.com/steipete/gogcli/internal/ui"
"google.golang.org/api/drive/v3"
)
func newDocsCmd(flags *rootFlags) *cobra.Command {
cmd := &cobra.Command{
Use: "docs",
Short: "Google Docs (export via Drive)",
}
cmd.AddCommand(newDocsExportCmd(flags))
cmd.AddCommand(newDocsInfoCmd(flags))
cmd.AddCommand(newDocsCreateCmd(flags))
cmd.AddCommand(newDocsCopyCmd(flags))
cmd.AddCommand(newDocsCatCmd(flags))
return cmd
}
func newDocsExportCmd(flags *rootFlags) *cobra.Command {
return newExportViaDriveCmd(flags, exportViaDriveOptions{
Use: "export <docId>",
Short: "Export a Google Doc (pdf|docx|txt)",
ArgName: "docId",
ExpectedMime: "application/vnd.google-apps.document",
KindLabel: "Google Doc",
DefaultFormat: "pdf",
FormatHelp: "Export format: pdf|docx|txt",
})
}
func newDocsInfoCmd(flags *rootFlags) *cobra.Command {
return newInfoViaDriveCmd(flags, infoViaDriveOptions{
Use: "info <docId>",
Short: "Get Google Doc metadata",
ArgName: "docId",
ExpectedMime: "application/vnd.google-apps.document",
KindLabel: "Google Doc",
})
}
func newDocsCreateCmd(flags *rootFlags) *cobra.Command {
var parent string
cmd := &cobra.Command{
Use: "create <title>",
Short: "Create a Google Doc",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
u := ui.FromContext(cmd.Context())
account, err := requireAccount(flags)
if err != nil {
return err
}
title := strings.TrimSpace(args[0])
if title == "" {
return usage("empty title")
}
svc, err := newDriveService(cmd.Context(), account)
if err != nil {
return err
}
f := &drive.File{
Name: title,
MimeType: "application/vnd.google-apps.document",
}
parent = strings.TrimSpace(parent)
if parent != "" {
f.Parents = []string{parent}
}
created, err := svc.Files.Create(f).
SupportsAllDrives(true).
Fields("id, name, mimeType, webViewLink").
Context(cmd.Context()).
Do()
if err != nil {
return err
}
if created == nil {
return errors.New("create failed")
}
if outfmt.IsJSON(cmd.Context()) {
return outfmt.WriteJSON(os.Stdout, map[string]any{"file": created})
}
u.Out().Printf("id\t%s", created.Id)
u.Out().Printf("name\t%s", created.Name)
u.Out().Printf("mime\t%s", created.MimeType)
if created.WebViewLink != "" {
u.Out().Printf("link\t%s", created.WebViewLink)
}
return nil
},
}
cmd.Flags().StringVar(&parent, "parent", "", "Destination folder ID")
return cmd
}
func newDocsCopyCmd(flags *rootFlags) *cobra.Command {
return newCopyViaDriveCmd(flags, copyViaDriveOptions{
Use: "copy <docId> <title>",
Short: "Copy a Google Doc",
ArgName: "docId",
ExpectedMime: "application/vnd.google-apps.document",
KindLabel: "Google Doc",
})
}
func newDocsCatCmd(flags *rootFlags) *cobra.Command {
var maxBytes int64
cmd := &cobra.Command{
Use: "cat <docId>",
Short: "Print a Google Doc as plain text",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
account, err := requireAccount(flags)
if err != nil {
return err
}
id := strings.TrimSpace(args[0])
if id == "" {
return usage("empty docId")
}
svc, err := newDriveService(cmd.Context(), account)
if err != nil {
return err
}
meta, err := svc.Files.Get(id).
SupportsAllDrives(true).
Fields("id, mimeType").
Context(cmd.Context()).
Do()
if err != nil {
return err
}
if meta == nil {
return errors.New("file not found")
}
if meta.MimeType != "application/vnd.google-apps.document" {
return fmt.Errorf("file is not a Google Doc (mimeType=%q)", meta.MimeType)
}
resp, err := driveExportDownload(cmd.Context(), svc, id, "text/plain")
if err != nil {
return err
}
if resp == nil || resp.Body == nil {
return errors.New("empty response")
}
defer resp.Body.Close()
var r io.Reader = resp.Body
if maxBytes > 0 {
r = io.LimitReader(resp.Body, maxBytes)
}
b, err := io.ReadAll(r)
if err != nil {
return err
}
if outfmt.IsJSON(cmd.Context()) {
return outfmt.WriteJSON(os.Stdout, map[string]any{"text": string(b)})
}
_, err = os.Stdout.Write(b)
return err
},
}
cmd.Flags().Int64Var(&maxBytes, "max-bytes", 2_000_000, "Max bytes to read (0 = unlimited)")
return cmd
}