package cmd import ( "fmt" "os" "github.com/spf13/cobra" "github.com/yeho/doks/internal/registry" "github.com/yeho/doks/internal/storage" ) var ( addKey string addPreserve bool addTags string addDescription string ) var addCmd = &cobra.Command{ Use: "add ", Short: "Add a document to doks", Long: `Add a document to doks by copying it to the storage directory. Use -p to create a symlink at the original location pointing to the stored file.`, Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { filePath := args[0] if addKey == "" { fmt.Fprintln(os.Stderr, "Error: -k (key) flag is required") os.Exit(1) } absPath, err := storage.GetAbsolutePath(filePath) if err != nil { fmt.Fprintf(os.Stderr, "Error resolving path: %v\n", err) os.Exit(1) } // Check if file exists if _, err := os.Stat(absPath); os.IsNotExist(err) { fmt.Fprintf(os.Stderr, "Error: file not found: %s\n", absPath) os.Exit(1) } // Load registry reg, err := registry.Load(cfg.RegistryPath()) if err != nil { fmt.Fprintf(os.Stderr, "Error loading registry: %v\n", err) os.Exit(1) } // Check if key already exists if reg.Exists(addKey) { fmt.Fprintf(os.Stderr, "Error: key '%s' already exists\n", addKey) os.Exit(1) } // Generate unique filename filename := storage.GenerateFilename(absPath) // Create storage and copy file store := storage.New(cfg.FilesDir()) if err := store.CopyFile(absPath, filename); err != nil { fmt.Fprintf(os.Stderr, "Error copying file: %v\n", err) os.Exit(1) } // Remove original and create symlink if preserve flag is set if addPreserve { if err := os.Remove(absPath); err != nil { fmt.Fprintf(os.Stderr, "Error removing original file: %v\n", err) // Clean up copied file store.RemoveFile(filename) os.Exit(1) } if err := store.CreateSymlink(absPath, filename); err != nil { fmt.Fprintf(os.Stderr, "Error creating symlink: %v\n", err) os.Exit(1) } } // Parse tags var tags []string if addTags != "" { tags = splitTags(addTags) } // Create registry entry entry := ®istry.Entry{ Key: addKey, Filename: filename, OriginalPath: absPath, HasSymlink: addPreserve, Tags: tags, Description: addDescription, } if err := reg.Add(entry); err != nil { fmt.Fprintf(os.Stderr, "Error adding to registry: %v\n", err) // Clean up store.RemoveFile(filename) if addPreserve { store.RemoveSymlink(absPath) } os.Exit(1) } fmt.Printf("Added '%s' with key '%s'\n", filePath, addKey) }, } func init() { rootCmd.AddCommand(addCmd) addCmd.Flags().StringVarP(&addKey, "key", "k", "", "Unique key for the document (required)") addCmd.Flags().BoolVarP(&addPreserve, "preserve", "p", false, "Create symlink at original location") addCmd.Flags().StringVarP(&addTags, "tags", "t", "", "Comma-separated tags") addCmd.Flags().StringVarP(&addDescription, "description", "d", "", "Description of the document") addCmd.MarkFlagRequired("key") } func splitTags(tags string) []string { if tags == "" { return nil } result := []string{} for _, tag := range splitString(tags, ",") { tag = trimSpace(tag) if tag != "" { result = append(result, tag) } } return result } func splitString(s, sep string) []string { var result []string start := 0 for i := 0; i < len(s); i++ { if i+len(sep) <= len(s) && s[i:i+len(sep)] == sep { result = append(result, s[start:i]) start = i + len(sep) } } result = append(result, s[start:]) return result } func trimSpace(s string) string { start := 0 end := len(s) for start < end && (s[start] == ' ' || s[start] == '\t') { start++ } for end > start && (s[end-1] == ' ' || s[end-1] == '\t') { end-- } return s[start:end] }