/*
Name : jsonctojson
Modified : 2022-06-30
$ go build -ldflags "-s -w" -trimpath .
*/
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"time"
"github.com/fsnotify/fsnotify"
"github.com/tidwall/jsonc"
)
func readAll(filename string) (b []byte, err error) {
file, err := os.Open(filename)
if err != nil {
return
}
defer file.Close()
return ioutil.ReadAll(file)
}
func backup(srcPath, dstPath string) error {
src, err := os.Open(srcPath)
if err != nil {
return err
}
defer src.Close()
dst, err := os.Create(dstPath)
if err != nil {
return err
}
defer dst.Close()
_, err = io.Copy(dst, src)
return err
}
func convert(srcJsonc string) error {
ext := filepath.Ext(srcJsonc)
if strings.Compare(strings.ToLower(ext), ".jsonc") != 0 {
puterr(fmt.Sprintf("not .jsonc: %s", srcJsonc))
return fmt.Errorf("not .jsonc")
}
dir := filepath.Dir(srcJsonc)
base := filepath.Base(srcJsonc)
outJson := filepath.Join(dir, base[:len(base)-len(ext)]) + ".json"
bakJson := filepath.Join(dir, base[:len(base)-len(ext)]) + ".bak"
state, err := os.Stat(srcJsonc)
if err != nil {
puterr(fmt.Sprintf("%s: %s", err, srcJsonc))
return err
}
b, err := readAll(srcJsonc)
if err != nil {
puterr(fmt.Sprintf("%s: %s", err, srcJsonc))
return err
}
b = jsonc.ToJSON(b)
var jsonAny interface{}
if err = json.Unmarshal(b, &jsonAny); err != nil {
puterr(fmt.Sprintf("%s: %s", err, srcJsonc))
return err
}
var formatedJSON bytes.Buffer
if err = json.Indent(&formatedJSON, b, "", " "); err != nil { // indent
puterr(fmt.Sprintf("%s: %s", err, srcJsonc))
return err
}
if err = backup(srcJsonc, bakJson); err != nil {
puterr(fmt.Sprintf("%s: %s", err, srcJsonc))
return err
}
if err = ioutil.WriteFile(outJson, formatedJSON.Bytes(), state.Mode()); err != nil {
puterr(fmt.Sprintf("%s: %s", err, srcJsonc))
return err
}
fmt.Println(formatedJSON.String())
puterr(fmt.Sprintf("%s ==> %s, backup: %s", srcJsonc, outJson, bakJson))
return err
}
func puterr(s string) {
fmt.Fprintf(os.Stderr, "%s[%s] %s\n", exeName, time.Now().Format("2006-01-02 15:04:05"), s)
}
func watch(wg *sync.WaitGroup) (watcher *fsnotify.Watcher, err error) {
if watcher, err = fsnotify.NewWatcher(); err != nil {
return
}
go func() {
defer watcher.Close()
wg.Add(1)
for {
select {
case event, ok := <-watcher.Events:
if !ok {
break
}
state, err := os.Stat(event.Name)
if err != nil {
puterr(fmt.Sprintf("%s", err))
continue
}
if state.IsDir() {
if event.Op&fsnotify.Create == fsnotify.Create {
watcher.Add(event.Name)
} else if event.Op&fsnotify.Remove == fsnotify.Remove {
watcher.Remove(event.Name)
} else if event.Op&fsnotify.Rename == fsnotify.Rename {
watcher.Add(event.Name) // to
watcher.Remove(event.String()) // from
}
} else {
if event.Op&fsnotify.Write == fsnotify.Write {
convert(event.Name)
}
}
case err, ok := <-watcher.Errors:
if !ok {
break
}
puterr(fmt.Sprintf("%s", err))
}
} // for
wg.Done()
}()
return
}
var exeName string
func main() {
exeName = filepath.Base(os.Args[0])
wg := &sync.WaitGroup{}
var (
flagWatcher bool
)
flag.BoolVar(&flagWatcher, "watch", false, "watching file or directory")
Usage := func() {
fmt.Fprintf(os.Stderr, "Usage: %s [options] <file|directory...>\n", exeName)
flag.PrintDefaults()
}
flag.Parse()
args := flag.Args()
if len(args) == 0 {
Usage()
os.Exit(1)
}
var err error
var w *fsnotify.Watcher
if flagWatcher {
w, err = watch(wg)
if err != nil {
puterr(fmt.Sprintf("%s", err))
os.Exit(1)
}
}
for _, path := range args {
puterr(fmt.Sprintf("%s", path))
state, err := os.Stat(path)
if err != nil {
puterr(fmt.Sprintf("%s", err))
continue
}
if !state.IsDir() {
convert(path)
if flagWatcher {
if err := w.Add(path); err != nil {
puterr(fmt.Sprintf("%s", err))
// os.Exit(1)
}
}
continue
}
if err = filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
if err != nil {
puterr(fmt.Sprintf("%s", err))
// os.Exit(1)
}
if info.IsDir() {
if flagWatcher {
if err := w.Add(path); err != nil {
puterr(fmt.Sprintf("%s", err))
// os.Exit(1)
}
}
return nil
}
convert(path)
return nil
}); err != nil {
puterr(fmt.Sprintf("%s", err))
os.Exit(1)
}
}
wg.Wait()
os.Exit(0)
}