-
Notifications
You must be signed in to change notification settings - Fork 0
/
jsonwatcher.go
161 lines (141 loc) · 3.44 KB
/
jsonwatcher.go
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
// Copyright 2023 Hany Mamdouh. All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.
package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"os/exec"
"os/signal"
"syscall"
"time"
"github.com/eiannone/keyboard"
"github.com/fsnotify/fsnotify"
)
var (
ctx context.Context
cancel context.CancelFunc
filePath string
predefFile string
)
func init() {
// Define the path to the JSON file you want to watch
predefFile = "your_file.json"
}
func main() {
// Set file to watch
flagFile := flag.String("f", predefFile, "file name to watch. Include path if not in current directory.")
flag.Parse()
filePath = *flagFile
fmt.Printf("Watching: %s\n", filePath)
// Handle Ctrl+C to gracefully exit the application
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT)
go watchCtrlC(sigCh)
// Create a new filesystem watcher
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
// Start watching the JSON file for changes
err = watcher.Add(filePath)
if err != nil {
log.Fatal(err)
}
// Create a context with cancellation
ctx, cancel = context.WithCancel(context.Background())
defer cancel()
// Create a channel to receive file change events
events := make(chan fsnotify.Event)
// Start a goroutine to listen for file change events
go fileWatcher(watcher, events)
// Start listening for keyboard input
err = keyboard.Open()
if err != nil {
log.Fatal(err)
}
defer keyboard.Close()
go watchForKeyExit(cancel)
// Loop to handle file change events
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
if event.Op&fsnotify.Write == fsnotify.Write {
fmt.Printf("Last File modified: %s\n", time.Now())
fmt.Println("Press: 'q' to quit")
// Cancel the context to signal the less goroutine to terminate
cancel()
// Start a new less process with a new context
ctx, cancel = context.WithCancel(context.Background())
go startLess(ctx, filePath)
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("Error:", err)
}
}
}
// Watch for ctrl+c to gracefully terminate
func watchCtrlC(sigCh chan os.Signal) {
<-sigCh
log.Println("Ctrl+C detected. Exiting...")
// Cancel the context to signal the less goroutine to terminate
cancel()
exec.Command("reset").Run()
os.Exit(0)
}
// Start new `less` instance for the watched file.
func startLess(ctx context.Context, filePath string) {
lessCmd := exec.CommandContext(ctx, "less", filePath)
lessCmd.Stdout = os.Stdout
lessCmd.Stdin = os.Stdin
lessCmd.Stderr = os.Stderr
err := lessCmd.Run()
if err != nil {
log.Println("Error running less:", err)
}
}
// File watcher infinite loop.
func fileWatcher(watcher *fsnotify.Watcher, events chan fsnotify.Event) {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
// Check if the event is a modification of the JSON file
if event.Op&fsnotify.Write == fsnotify.Write {
events <- event
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("error:", err)
}
}
}
// Watch for `q` key to clean exit.
func watchForKeyExit(cancel context.CancelFunc) {
for {
char, _, err := keyboard.GetKey()
if err != nil {
log.Fatal(err)
}
if char == 'q' || char == 'Q' {
log.Println("Q key detected. Exiting...")
cancel()
exec.Command("reset").Run()
os.Exit(0)
}
}
}