summaryrefslogtreecommitdiff
path: root/cmd/ls.go
blob: bcdbe661026ee7f6dea52bf6e4436eb24338e89b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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")
}