diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/cat.go | 94 | ||||
-rw-r--r-- | cmd/export.go | 123 | ||||
-rw-r--r-- | cmd/hash.go | 98 | ||||
-rw-r--r-- | cmd/import.go | 203 | ||||
-rw-r--r-- | cmd/info.go | 94 | ||||
-rw-r--r-- | cmd/ls.go | 168 | ||||
-rw-r--r-- | cmd/root.go | 36 | ||||
-rw-r--r-- | cmd/tag/add.go | 88 | ||||
-rw-r--r-- | cmd/tag/remove.go | 87 | ||||
-rw-r--r-- | cmd/tag/show.go | 82 | ||||
-rw-r--r-- | cmd/tag/tag.go | 63 |
11 files changed, 1136 insertions, 0 deletions
diff --git a/cmd/cat.go b/cmd/cat.go new file mode 100644 index 0000000..688f10f --- /dev/null +++ b/cmd/cat.go @@ -0,0 +1,94 @@ +/* +Copyright © 2025 NAME HERE <EMAIL ADDRESS> +*/ +package cmd + +import ( + "fmt" + "os" + + "encoding/json" + + "github.com/scrotadamus/ghligh/document" + "github.com/spf13/cobra" +) + +// catCmd represents the cat command +var catCmd = &cobra.Command{ + Use: "cat", + Short: "cat prints highlights of pdf files [unix][json]", + Long: ` + ghligh cat file1.pdf file2.pdf ... [--json] [-i] + + will show every highlights inside pdf files specified + if --json is set the output will be in json format + + if -i is set the json output will be indented +`, + Run: func(cmd *cobra.Command, args []string) { + + useJSON, err := cmd.Flags().GetBool("json") + if err != nil { + cmd.Help() + return + } + + indent, err := cmd.Flags().GetBool("indent") + if err != nil { + cmd.Help() + return + } + + jsonCat := make(map[string][]document.HighlightedText) + + // for every arg + for _, arg := range args { + doc, err := document.Open(arg) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + continue + } + + highlights := doc.Cat() + if !useJSON { + for _, highlight := range highlights { + if highlight.Contents != "" { + fmt.Printf("%s {{{%s}}}", highlight.Text, highlight.Contents) + } else { + fmt.Printf("%s", highlight.Text) + } + } + } else { + jsonCat[doc.Path] = highlights + } + + doc.Close() + } + + var jsonBytes []byte + if indent { + jsonBytes, err = json.MarshalIndent(jsonCat, "", " ") + } else { + jsonBytes, err = json.Marshal(jsonCat) + } + if err != nil { + panic(err) + } + fmt.Println(string(jsonBytes)) + }, +} + +func init() { + rootCmd.AddCommand(catCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // catCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + catCmd.Flags().BoolP("json", "j", false, "print highlights as json") + catCmd.Flags().BoolP("indent", "i", false, "print highlights as json") +} diff --git a/cmd/export.go b/cmd/export.go new file mode 100644 index 0000000..aaf822c --- /dev/null +++ b/cmd/export.go @@ -0,0 +1,123 @@ +/* +Copyright © 2025 NAME HERE <EMAIL ADDRESS> +*/ +package cmd + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/scrotadamus/ghligh/document" + "github.com/spf13/cobra" +) + +var outputFiles []string + +func writeJSONToFile(jsonBytes []byte, path string) error { + file, err := os.Create(path) + if err != nil { + return err + } + defer file.Close() + + // IF FILE EXISTS ASK + _, err = file.Write(jsonBytes) + if err != nil { + return err + } + + return nil +} + +// exportCmd represents the export command +var exportCmd = &cobra.Command{ + Use: "export", + Short: "export pdf highlights into json", + Long: ` + ghligh export foo.pdf bar.pdf ... [--to fnord.json] [-1] [-i] + + will create one or more json file (specified with --to) or dump it + to stdout (-1) + + -i will indent the json output +`, + + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + cmd.Help() + return + } + + indent, err := cmd.Flags().GetBool("indent") + if err != nil { + cmd.Help() + return + } + + stdout, err := cmd.Flags().GetBool("stdout") + if err != nil { + cmd.Help() + return + } + + if !stdout && len(outputFiles) == 0 { + fmt.Fprintf(os.Stderr, "nowhere to put output I am not doing anything\n") + return + } + + var exportedDocs []document.GhlighDoc + for _, file := range args { + doc, err := document.Open(file) + if err != nil { + fmt.Fprintf(os.Stderr, "error loading %s: %v", file, err) + continue + } + + doc.AnnotsBuffer = doc.GetAnnotsBuffer() + doc.HashBuffer = doc.HashDoc() + exportedDocs = append(exportedDocs, *doc) + } + + var jsonBytes []byte + if indent { + jsonBytes, err = json.MarshalIndent(exportedDocs, "", " ") + } else { + jsonBytes, err = json.Marshal(exportedDocs) + } + if err != nil { + panic(err) + } + + for _, file := range outputFiles { + err := writeJSONToFile(jsonBytes, file) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + } + } + + if stdout { + fmt.Printf("%s\n", string(jsonBytes)) + } + + }, +} + +func init() { + rootCmd.AddCommand(exportCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // exportCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // exportCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + // TODO flag toFiles + exportCmd.Flags().BoolP("indent", "i", false, "indent the json data") + exportCmd.Flags().BoolP("stdout", "1", false, "dump to stdout") + + exportCmd.Flags().StringArrayVarP(&outputFiles, "to", "t", []string{}, "files to save exported annots") +} diff --git a/cmd/hash.go b/cmd/hash.go new file mode 100644 index 0000000..5185736 --- /dev/null +++ b/cmd/hash.go @@ -0,0 +1,98 @@ +/* +Copyright © 2025 NAME HERE <EMAIL ADDRESS> +*/ +package cmd + +import ( + "encoding/json" + "fmt" + "os" + + "sync" + + "github.com/scrotadamus/ghligh/document" + "github.com/spf13/cobra" +) + +// hashCmd represents the hash command +var hashCmd = &cobra.Command{ + Use: "hash", + Short: "display the ghligh hash used to identify a documet [json]", + Long: `the ghligh hash is used to identify documents with different filenames / annotations and it is calculated using the text of some pages. + + ghligh hash file1.json file2.json [-i] + + -i will indent the json output + `, + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + cmd.Help() + return + } + indent, err := cmd.Flags().GetBool("indent") + if err != nil { + cmd.Help() + return + } + + // var hashes []document.GhlighDoc + hashChan := make(chan document.GhlighDoc) + var wg sync.WaitGroup + wg.Add(len(args)) + + for _, arg := range args { + go func(arg string) { + defer wg.Done() + doc, err := document.Open(arg) + if err != nil { + fmt.Fprintf(os.Stderr, "error opening %s: %v\n", arg, err) + return + } + + // A little hacky, set hash after closing the document + //doc.Close() + doc.HashBuffer = doc.HashDoc() + + hashChan <- *doc + }(arg) + } + + go func() { + wg.Wait() + close(hashChan) + }() + + var hashes []document.GhlighDoc + for doc := range hashChan { + hashes = append(hashes, doc) + doc.Close() + } + + var jsonBytes []byte + if indent { + jsonBytes, err = json.MarshalIndent(hashes, "", " ") + } else { + jsonBytes, err = json.Marshal(hashes) + } + if err != nil { + panic(err) + } + fmt.Println(string(jsonBytes)) + + }, +} + +func init() { + rootCmd.AddCommand(hashCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // hashCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // hashCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + hashCmd.Flags().BoolP("indent", "i", false, "indent the json data") +} diff --git a/cmd/import.go b/cmd/import.go new file mode 100644 index 0000000..2bcb1d1 --- /dev/null +++ b/cmd/import.go @@ -0,0 +1,203 @@ +/* +Copyright © 2025 NAME HERE <EMAIL ADDRESS> +*/ +package cmd + +import ( + "encoding/json" + "fmt" + "io" + "os" + "sync" + + "github.com/scrotadamus/ghligh/document" + "github.com/spf13/cobra" + + "crypto/sha256" +) + +var inputFiles []string + +type importedAnnots struct { + // FIXME just internatl map[string]struct where + // struct contains map[string]bool and document.AnnotsMap + internal map[string]document.AnnotsMap + annotsHashes map[string]map[string]bool + mutex sync.Mutex +} + +func (ia *importedAnnots) get(hash string) document.AnnotsMap { + return ia.internal[hash] +} + +func (ia *importedAnnots) init(hash string) { + ia.mutex.Lock() + defer ia.mutex.Unlock() + if ia.internal[hash] == nil { + ia.internal[hash] = make(document.AnnotsMap) + } + if ia.annotsHashes[hash] == nil { + ia.annotsHashes[hash] = make(map[string]bool) + } +} + +func (ia *importedAnnots) check(docHash string, annotsHash string) bool { + ia.mutex.Lock() + defer ia.mutex.Unlock() + ok := ia.annotsHashes[docHash][annotsHash] + return ok +} + +func (ia *importedAnnots) insert(hash string, am document.AnnotsMap) error { + amHash, err := hashAnnotsMap(am) + if err != nil { + return err + } + + present := ia.check(hash, amHash) + if !present { + ia.mutex.Lock() + for key, value := range am { + ia.internal[hash][key] = append(ia.internal[hash][key], value...) + ia.annotsHashes[hash][amHash] = true + } + ia.mutex.Unlock() + } + return nil +} + +func hashAnnotsMap(am document.AnnotsMap) (string, error) { + jsonBytes, err := json.Marshal(am) + if err != nil { + return "", err + } + + h := sha256.Sum256(jsonBytes) + return fmt.Sprintf("%x", h), nil +} + +func loadImportedAnnots(ia *importedAnnots, reader io.Reader) { + data, err := io.ReadAll(reader) + if err != nil { + fmt.Fprintf(os.Stderr, "could not read data: %v\n", err) + return + } + + var importedDocs []document.GhlighDoc + + err = json.Unmarshal(data, &importedDocs) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + return + } + + for _, importedDoc := range importedDocs { + hash := importedDoc.HashBuffer + ia.init(hash) + ia.insert(hash, importedDoc.AnnotsBuffer) + } +} + +// importCmd represents the import command +var importCmd = &cobra.Command{ + Use: "import", + Short: "import highlights from json file", + Long: ` + ghligh import foo.pdf bar.pdf ... [--from fnord.json] [--from kadio.json] [-0] [--save=false] + + will import into foo.pdf bar.pdf etc... the highlights from file specified + with the --from flag + + if -0 is set ghligh will read json from stdin + + --save=false will run without saving documents, it will just tells you how + many annotations from the json files specified will be imported +`, + + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + cmd.Help() + return + } + + stdin, err := cmd.Flags().GetBool("stdin") + if err != nil { + cmd.Help() + return + } + + save, err := cmd.Flags().GetBool("save") + if err != nil { + cmd.Help() + return + } + + if stdin == false && len(inputFiles) == 0 { + fmt.Fprintf(os.Stderr, "nowhere to put output I am not doing anything\n") + return + } + + // Load Annot Maps + ia := importedAnnots{ + internal: make(map[string]document.AnnotsMap), + annotsHashes: make(map[string]map[string]bool), + } + + var wg sync.WaitGroup + + wg.Add(len(inputFiles)) + for _, file := range inputFiles { + //wg.Add(1) + go func(path string) { + defer wg.Done() + + f, err := os.Open(path) + if err != nil { + fmt.Fprintf(os.Stderr, "could not open file %s: %v\n", path, err) + return + } + defer f.Close() + + loadImportedAnnots(&ia, f) + }(file) + } + + wg.Wait() + + if stdin { + loadImportedAnnots(&ia, os.Stdin) + } + + // load from inputFiles + for _, file := range args { + doc, err := document.Open(file) + if err != nil { + fmt.Fprintf(os.Stderr, "error loading %s: %v", file, err) + continue + } + + hash := doc.HashDoc() + + num, err := doc.Import(ia.get(hash)) + if err != nil { + fmt.Fprintf(os.Stderr, "could not import highlights into %s: %v\n", file, err) + } else { + fmt.Fprintf(os.Stderr, "imported %d annots into %s\n", num, file) + if save { + doc.Save() + } + } + doc.Close() + + } + + }, +} + +func init() { + rootCmd.AddCommand(importCmd) + + importCmd.Flags().BoolP("stdin", "0", false, "read json from stdin") + importCmd.Flags().BoolP("save", "", true, "save the file with new annotation importer") + importCmd.Flags().StringArrayVarP(&inputFiles, "from", "f", []string{}, "files to import annots from") +} diff --git a/cmd/info.go b/cmd/info.go new file mode 100644 index 0000000..7a33f62 --- /dev/null +++ b/cmd/info.go @@ -0,0 +1,94 @@ +/* +Copyright © 2025 NAME HERE <EMAIL ADDRESS> +*/ +package cmd + +import ( + "fmt" + "os" + + "sync" + + "encoding/json" + + "github.com/scrotadamus/ghligh/document" + "github.com/scrotadamus/ghligh/go-poppler" + "github.com/spf13/cobra" +) + +// infoCmd represents the info command +var infoCmd = &cobra.Command{ + Use: "info", + Short: "display info about pdf documents [json]", + Long: ` + ghligh info file1.pdf file2.pdf [-i] + + shows information about pdf (author, publisher, modification date, etc...) + -i will indent the json output + `, + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + cmd.Help() + return + } + indent, err := cmd.Flags().GetBool("indent") + if err != nil { + cmd.Help() + return + } + + infoChan := make(chan poppler.DocumentInfo) + var wg sync.WaitGroup + wg.Add(len(args)) + + for _, arg := range args { + go func(arg string) { + defer wg.Done() + doc, err := document.Open(arg) + if err != nil { + fmt.Fprintf(os.Stderr, "error opening %s: %v\n", arg, err) + return + } + defer doc.Close() + infoChan <- doc.Info() + }(arg) + } + + go func() { + wg.Wait() + close(infoChan) + }() + + var infos []poppler.DocumentInfo + for info := range infoChan { + infos = append(infos, info) + } + + var jsonBytes []byte + if indent { + jsonBytes, err = json.MarshalIndent(infos, "", " ") + } else { + jsonBytes, err = json.Marshal(infos) + } + if err != nil { + panic(err) + } + fmt.Println(string(jsonBytes)) + + }, +} + +func init() { + rootCmd.AddCommand(infoCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // infoCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // infoCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + infoCmd.Flags().BoolP("indent", "i", false, "indent the json data") +} diff --git a/cmd/ls.go b/cmd/ls.go new file mode 100644 index 0000000..bcdbe66 --- /dev/null +++ b/cmd/ls.go @@ -0,0 +1,168 @@ +/* +Copyright © 2025 NAME HERE <EMAIL ADDRESS> +*/ +package cmd + +import ( + "context" + "fmt" + "os" + "path/filepath" + "sync" + + "github.com/scrotadamus/ghligh/document" + "github.com/spf13/cobra" +) + +var recursive bool + +type resolver struct { + paths []string + recurse bool + ctx context.Context + ch chan<- string + wg sync.WaitGroup +} + +func (r *resolver) resolve() { + for _, path := range r.paths { + r.wg.Add(1) + go r.resolvePath(path) + } + + go func() { + r.wg.Wait() + close(r.ch) + }() +} + +func (r *resolver) resolvePath(path string) { + defer r.wg.Done() + if err := r.ctx.Err(); err != nil { + return + } + + entries, err := os.ReadDir(path) + if err != nil { + str, err := filepath.Abs(path) + if err != nil { + return + } + + r.ch <- str + return + } + + for _, entry := range entries { + info, err := entry.Info() + if err != nil { + fmt.Fprintf(os.Stderr, "Error retrieving info for %s: %v\n", entry.Name(), err) + continue + } + + fullPath := filepath.Join(path, entry.Name()) + + if info.IsDir() { + if r.recurse { + if err := r.ctx.Err(); err != nil { + return + } + r.wg.Add(1) + go r.resolvePath(fullPath) + } + } else if info.Mode().IsRegular() { + r.ch <- fullPath + } + } +} + +func lsArgs(args []string) []string { + if len(args) == 0 { + cwd, err := os.Getwd() + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(1) + return nil + } + return []string{cwd} + + } + return args +} + +func checkFile(path string) bool { + doc, err := document.Open(path) + if err != nil { + return false + } + defer doc.Close() + + return doc.HasHighlights() +} + +// lsCmd represents the ls command +var lsCmd = &cobra.Command{ + Use: "ls", + Short: "show files with highlights or tagged with 'ls' [unix]", + Long: ` + ghligh ls file1.pdf directory [-R] [-c] + + will show every file inside directory that contains highlights or it is marked + ls with the ghligh tag add command + + ghligh ls # show files in current dir + ghligh ls file1.pdf # if it outpus file1.pdf it means that file1.pdf contains highlights + ghligh ls -c file1.pdf # same as ghligh ls file1.pdf but exit status will not be if file1.pdf doesnt + contains highlights + + ghligh ls -R # do it recursively, be careful with symlink dir cycles, as I am to lazy to + address that particular issue +`, + Run: func(cmd *cobra.Command, args []string) { + files := lsArgs(args) + ch := make(chan string) + ctx := context.Background() + + res := resolver{ + paths: files, + recurse: recursive, + ctx: ctx, + ch: ch, + } + + go res.resolve() + + var wg sync.WaitGroup + var found bool + for file := range ch { + wg.Add(1) + go func(f string) { + defer wg.Done() + if checkFile(f) { + found = true + fmt.Printf("%s\n", f) + } + }(file) + } + wg.Wait() + + check, err := cmd.Flags().GetBool("check") + if err != nil { + cmd.Help() + return + } + if check && !found { + os.Exit(1) + } + + }, +} + +func init() { + rootCmd.AddCommand(lsCmd) + + lsCmd.Flags().BoolVarP(&recursive, "recursive", "R", false, "List recursively") + lsCmd.Flags().BoolP("check", "c", false, "exit status is 1 if no file its found") + // order pdf by time of something (modification / creation) ??? + //lsCmd.Flags().BoolP("time", "t", false, "ls by time") +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..321253c --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,36 @@ +/* +Copyright © 2025 Francesco Orlando scrotadamus@insiberia.net +*/ +package cmd + +import ( + "os" + + "github.com/scrotadamus/ghligh/cmd/tag" + "github.com/spf13/cobra" +) + +var rootCmd = &cobra.Command{ + Use: "ghligh", + Short: "pdf highlights swiss knife", + Long: `ghligh can be used to manipulate pdf files in various ways.`, + + Run: func(cmd *cobra.Command, args []string) { + cmd.Help() + return + }, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} + +func init() { + //rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + rootCmd.AddCommand(tag.TagCmd) +} diff --git a/cmd/tag/add.go b/cmd/tag/add.go new file mode 100644 index 0000000..a5dc258 --- /dev/null +++ b/cmd/tag/add.go @@ -0,0 +1,88 @@ +/* +Copyright © 2025 NAME HERE <EMAIL ADDRESS> +*/ +package tag + +import ( + "fmt" + "io" + "os" + + "github.com/scrotadamus/ghligh/document" + "github.com/spf13/cobra" +) + +func readStdin() string { + data, err := io.ReadAll(os.Stdin) + if err != nil { + panic(err) + } + return string(data) +} + +// tagAddCmd represents the tag command +var tagAddCmd = &cobra.Command{ + Use: "add", + Short: "add a ghligh tag to a pdf file", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + cmd.Help() + return + } + + stdin, err := cmd.Flags().GetBool("stdin") + if err != nil { + cmd.Help() + return + } + if stdin { + tags = append(tags, readStdin()) + } + if len(tags) == 0 { + fmt.Fprintf(os.Stderr, "Either --tag or --stdin is required\n", err) + os.Exit(1) + } + + for _, file := range args { + doc, err := document.Open(file) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + continue + } + for _, tag := range tags { + doc.Tag(tag) + fmt.Fprintf(os.Stderr, "added tag {{{%s}}} to %s\n", tag, file) + } + doc.Save() + doc.Close() + } + }, +} + +func init() { + TagCmd.AddCommand(tagAddCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // tagCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // tagCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + // TODO add to "add" command + tagAddCmd.Flags().StringArrayVarP(&tags, "tag", "t", []string{}, "Tag da associare ai file (può essere usato più volte)") + tagAddCmd.Flags().BoolP("stdin", "0", false, "read tag from stdin") + + //if err := tagAddCmd.MarkFlagRequired("tag"); err != nil { + //panic(err) + //} + +} diff --git a/cmd/tag/remove.go b/cmd/tag/remove.go new file mode 100644 index 0000000..ca0f1b4 --- /dev/null +++ b/cmd/tag/remove.go @@ -0,0 +1,87 @@ +/* +Copyright © 2025 NAME HERE <EMAIL ADDRESS> + +*/ +package tag + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/scrotadamus/ghligh/document" +) + +var tagRemoveCmd = &cobra.Command{ + Use: "remove", + Short: "remove ghligh tags from a pdf files using regex", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + cmd.Help() + return + } + + if regex == "" && exact == "" { + fmt.Fprintf(os.Stderr, "either regex or exact must be set with --regex or --exact\n") + os.Exit(1) + } + + nosafe, err := cmd.Flags().GetBool("nosafe") + if err != nil { + cmd.Help() + return + } + + // just a little hack -> boundaries = nosafe ? "" : `\b` + boundaries := map[bool]string{true: "", false: `\b`}[nosafe] + regex = formatRegex(regex, boundaries) + + // if exact set overwrite regex + if exact != "" { + regex = `^` + exact + `$` + } + + for _, file := range(args){ + doc, err := document.Open(file) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + continue + } + + tags := regexSlice(regex, doc.GetTags()) + removedTags := doc.RemoveTags(tags) + doc.Save() + doc.Close() + + fmt.Printf("removed %d tags from %s\n", removedTags, doc.Path) + } + + }, +} + +func init() { + TagCmd.AddCommand(tagRemoveCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // removetagsCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // removetagsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + tagRemoveCmd.Flags().StringVarP(®ex, "regex", "r", "", "regex") + tagRemoveCmd.Flags().StringVarP(&exact, "exact", "e", "", "exact") + tagRemoveCmd.Flags().BoolP("nosafe", "", false, "don't use safe boundaries around regex") + + //if err := tagRemoveCmd.MarkFlagRequired("regex"); err != nil { + //panic(err) + //} +} diff --git a/cmd/tag/show.go b/cmd/tag/show.go new file mode 100644 index 0000000..3d8ae62 --- /dev/null +++ b/cmd/tag/show.go @@ -0,0 +1,82 @@ +/* +Copyright © 2025 NAME HERE <EMAIL ADDRESS> + +*/ +package tag + +import ( + "fmt" + "os" + + "encoding/json" + "github.com/spf13/cobra" + "github.com/scrotadamus/ghligh/document" +) + +var tagShowCmd = &cobra.Command{ + Use: "show", + Short: "show ghligh tags of pdf files [json]", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + cmd.Help() + return + } + + indent, err := cmd.Flags().GetBool("indent") + if err != nil { + cmd.Help() + return + } + + exportTags := make(map[string][]string) + for _, file := range(args){ + doc, err := document.Open(file) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + continue + } + + if regex != "" { + regex = formatRegex(regex, "") + } + tags := regexSlice(regex, doc.GetTags()) + + exportTags[doc.Path] = tags + + doc.Close() + + } + + var jsonBytes []byte + if indent { + jsonBytes, err = json.MarshalIndent(exportTags, "", " ") + } else { + jsonBytes, err = json.Marshal(exportTags) + } + if err != nil { + panic(err) + } + fmt.Println(string(jsonBytes)) + }, +} + +func init() { + TagCmd.AddCommand(tagShowCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // showtagsCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + tagShowCmd.Flags().BoolP("indent", "i", false, "indent the json data") + tagShowCmd.Flags().StringVarP(®ex, "regex", "r", "", "regex") +} diff --git a/cmd/tag/tag.go b/cmd/tag/tag.go new file mode 100644 index 0000000..30e41c8 --- /dev/null +++ b/cmd/tag/tag.go @@ -0,0 +1,63 @@ +/* +Copyright © 2025 NAME HERE <EMAIL ADDRESS> + +*/ +package tag + +import ( + "regexp" + "github.com/spf13/cobra" +) + + +var tags []string + +var regex, exact string + +func formatRegex(r string, boundaries string) string { + //return `\b` + r + `\b` + return boundaries + r + boundaries +} + +func regexSlice(regex string, slice []string) []string { + if regex == "" { + return slice + } + + var newSlice []string + re, err := regexp.Compile(regex) + if err != nil { + panic(err) + } + for _, s := range(slice){ + if re.MatchString(s){ + newSlice = append(newSlice, s) + } + } + + return newSlice +} + +// tagCmd represents the tag command +var TagCmd = &cobra.Command{ + Use: "tag", + Short: "manage pdf tags", + Long: `a tag is a string you can attach to a pdf +.`, +} + +func init() { + //rootCmd.AddCommand(tagCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // tagCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // tagCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + // TODO add to "add" command + // tagCmd.Flags().StringArrayVarP(&tags, "tag", "t", []string{}, "Tag da associare ai file (può essere usato più volte)") +} |