diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index c671fe2..ed9269b 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -21,7 +21,7 @@ jobs: with: go-version: '1.20' - - run: go build -o bin/${{ github.event.repository.name }}_linux_x64 . + - run: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/${{ github.event.repository.name }}_linux_x64 . - run: CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o bin/${{ github.event.repository.name }}_win_x64.exe - run: CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -o bin/${{ github.event.repository.name }}_win_arm64.exe . - run: CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o bin/${{ github.event.repository.name }}_darwin_x64 . diff --git a/README.md b/README.md index 6396c5a..ae85070 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,33 @@ excel merge diff tools,excel对比合并工具,把excel转成csv格式,然 ### 支持功能 -- 支持xlsx、xlsm、xls +- 支持xlsx、xlsm、xls,支持多Sheet - 支持excel对比 diff - 支持excel合并 merge - 支持作为TortoiseSVN和TortoiseGit以及其他能够自定义对比工具的版本控制软件,如果对比的不是excel文件,会直接调用对比工具。 +## 使用方法 + +### windows + +双击打开执行文件,允许权限(用于读取和修改注册表) + +1. 选择基础的对比工具之后回车确定,如果有安装Beyond Comapre会识别出来 + + ![select_comapre](doc/img/select_comapre.png) + +2. 选择需要注册的scm版本工具,空格选择,回车确定,默认全选 + + ![select_register](doc/img/select_register.png) + +3. 注册完成,任意键关闭 + + ![complete_register](E:\program\my\go\excel_merge\doc\img\complete_register.png) + + 4. 就可以正常使用了,比较示例如下,合并同理: + + ![diff](E:\program\my\go\excel_merge\doc\img\diff.png) + ## TODO -- excel合并优化,考虑更多情况,保留更多格式数据。 +- excel合并保留更多格式数据。 diff --git a/config/config.go b/config/config.go index 643d306..0e3f35f 100644 --- a/config/config.go +++ b/config/config.go @@ -1,8 +1,13 @@ package config import ( - "excel_merge/util" + "fmt" + "github.com/821869798/fankit/fanpath" "github.com/BurntSushi/toml" + "os" + "regexp" + "runtime" + "strings" ) type RawGlobalConfig struct { @@ -13,13 +18,58 @@ type RawGlobalConfig struct { MergeArgs string `toml:"merge_arg"` } -var GlobalConfig *RawGlobalConfig +var ( + GlobalConfig *RawGlobalConfig + ConfigFilePath string +) func ParseConfig(configFile string) error { - configFile = util.AbsOrRelExecutePath(configFile) + ConfigFilePath = fanpath.AbsOrRelExecutePath(configFile) GlobalConfig = new(RawGlobalConfig) - if _, err := toml.DecodeFile(configFile, GlobalConfig); err != nil { + if _, err := toml.DecodeFile(ConfigFilePath, GlobalConfig); err != nil { + return err + } + return nil +} + +func WriteConfig(configFile string) error { + if configFile == "" { + configFile = ConfigFilePath + } + f, err := os.Create(configFile) + if err != nil { + return err + } + if err := toml.NewEncoder(f).Encode(GlobalConfig); err != nil { + return err + } + return nil +} + +func UpdateCompareTool(newFilePath string) error { + content, err := os.ReadFile(ConfigFilePath) + if err != nil { + return err + } + + // 定义正则表达式 + pattern := `compare_exe\s*=\s*"(.*?)"` + regex := regexp.MustCompile(pattern) + + if runtime.GOOS == "windows" { + // windows系统下,将路径中的反斜杠替换为双反斜杠 + newFilePath = strings.ReplaceAll(newFilePath, "\\", "\\\\") + } + // 使用正则表达式替换字符串 + newContent := regex.ReplaceAllString(string(content), fmt.Sprintf("compare_exe = \"%s\"", newFilePath)) + + // 将替换后的内容写入文件 + err = os.WriteFile(ConfigFilePath, []byte(newContent), 0644) + if err != nil { return err } return nil + + // 选择需要注册的版本软件工具 + } diff --git a/convert/common.go b/convert/common.go index 8714512..a073bb2 100644 --- a/convert/common.go +++ b/convert/common.go @@ -4,8 +4,8 @@ import ( "bufio" "bytes" "encoding/csv" - "excel_merge/define" "fmt" + "github.com/821869798/excel_merge/define" "io" "os" "strings" diff --git a/convert/csv.go b/convert/csv.go index 4c230eb..e00f0fe 100644 --- a/convert/csv.go +++ b/convert/csv.go @@ -1,7 +1,7 @@ package convert import ( - "excel_merge/define" + "github.com/821869798/excel_merge/define" ) type ConvertCSV struct { diff --git a/convert/entry.go b/convert/entry.go index 269859c..7478a38 100644 --- a/convert/entry.go +++ b/convert/entry.go @@ -2,8 +2,8 @@ package convert import ( "errors" - "excel_merge/define" "fmt" + "github.com/821869798/excel_merge/define" ) func RunConvert(mode string, excelData *define.ExcelData, filePath string) error { diff --git a/convert/register.go b/convert/register.go index ab61582..e0e4387 100644 --- a/convert/register.go +++ b/convert/register.go @@ -1,7 +1,7 @@ package convert import ( - "excel_merge/define" + "github.com/821869798/excel_merge/define" ) var convertModes map[string]define.IConvert diff --git a/define/system.go b/define/system.go new file mode 100644 index 0000000..a35562b --- /dev/null +++ b/define/system.go @@ -0,0 +1,10 @@ +package define + +type SystemType int + +const ( + SystemTypeNone = iota + SystemTypeWindows + SystemTypeLinux + SystemTypeMac +) diff --git a/diff/diff.go b/diff/diff.go index d405228..de12471 100644 --- a/diff/diff.go +++ b/diff/diff.go @@ -1,12 +1,13 @@ package diff import ( - "excel_merge/config" - "excel_merge/convert" - "excel_merge/define" - "excel_merge/source" - "excel_merge/util" "fmt" + "github.com/821869798/excel_merge/config" + "github.com/821869798/excel_merge/convert" + "github.com/821869798/excel_merge/define" + "github.com/821869798/excel_merge/source" + "github.com/821869798/fankit/fanpath" + "github.com/821869798/fankit/fanstr" "github.com/gookit/slog" "os" "os/exec" @@ -28,8 +29,8 @@ func Run(fileList []string) { defer os.Remove(file2) } - diffArg := util.FormatFieldName(config.GlobalConfig.DiffArgs, "left", file1, "right", file2) - cmd := exec.Command(util.AbsOrRelExecutePath(config.GlobalConfig.CompareTools), diffArg...) + diffArg := fanstr.FormatFieldName(config.GlobalConfig.DiffArgs, "left", file1, "right", file2) + cmd := exec.Command(fanpath.AbsOrRelExecutePath(config.GlobalConfig.CompareTools), diffArg...) output, err := cmd.CombinedOutput() exitCode := cmd.ProcessState.ExitCode() if nil != err && !define.IsCompareExitCodeSafe(config.GlobalConfig.CompareTools, exitCode) { @@ -62,7 +63,7 @@ func convertFile(file string) string { return "" } - err = util.CreateDirIfNoExist(filepath.Dir(outputFile)) + err = fanpath.CreateDirIfNoExist(filepath.Dir(outputFile)) if err != nil { slog.Panicf("[diff] %v", err) return "" diff --git a/diff/diff_test.go b/diff/diff_test.go index 54f81f4..1691314 100644 --- a/diff/diff_test.go +++ b/diff/diff_test.go @@ -1,7 +1,8 @@ package diff import ( - "excel_merge/util" + "github.com/821869798/fankit/fanpath" + "github.com/821869798/fankit/fanstr" "os" "strings" "testing" @@ -25,8 +26,8 @@ func TestFunc1(t *testing.T) { file := "my file.txt" err := "file not found" - result := util.FormatFieldName("File {file} had error {error}", "file", file, "error", err) + result := fanstr.FormatFieldName("File {file} had error {error}", "file", file, "error", err) t.Logf(strings.Join(result, "|")) - t.Logf(util.GetFileNameWithoutExt("qwe.txt")) + t.Logf(fanpath.GetFileNameWithoutExt("qwe.txt")) } diff --git a/doc/img/complete_register.png b/doc/img/complete_register.png new file mode 100644 index 0000000..22df896 Binary files /dev/null and b/doc/img/complete_register.png differ diff --git a/doc/img/diff.png b/doc/img/diff.png new file mode 100644 index 0000000..102c0be Binary files /dev/null and b/doc/img/diff.png differ diff --git a/doc/img/select_comapre.png b/doc/img/select_comapre.png new file mode 100644 index 0000000..b487c54 Binary files /dev/null and b/doc/img/select_comapre.png differ diff --git a/doc/img/select_register.png b/doc/img/select_register.png new file mode 100644 index 0000000..642ca6c Binary files /dev/null and b/doc/img/select_register.png differ diff --git a/go.mod b/go.mod index 1d5c0db..372ee4c 100644 --- a/go.mod +++ b/go.mod @@ -1,27 +1,54 @@ -module excel_merge +module github.com/821869798/excel_merge go 1.20 require ( + github.com/821869798/fankit v0.0.2 + github.com/AlecAivazis/survey/v2 v2.3.7 github.com/BurntSushi/toml v1.3.2 github.com/gookit/slog v0.5.4 + github.com/ncruces/zenity v0.10.10 github.com/xuri/excelize/v2 v2.7.1 + golang.org/x/sys v0.10.0 ) require ( + github.com/akavel/rsrc v0.10.2 // indirect + github.com/dchest/jsmin v0.0.0-20220218165748-59f39799265f // indirect + github.com/getlantern/byteexec v0.0.0-20220903141943-7db46f110fbc // indirect + github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect + github.com/getlantern/elevate v0.0.0-20220903142053-479ab992b264 // indirect + github.com/getlantern/errors v1.0.1 // indirect + github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c // indirect + github.com/getlantern/golog v0.0.0-20211223150227-d4d95a44d873 // indirect + github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect + github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect + github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect + github.com/go-stack/stack v1.8.0 // indirect github.com/gookit/color v1.5.4 // indirect github.com/gookit/goutil v0.6.12 // indirect github.com/gookit/gsr v0.1.0 // indirect + github.com/josephspurrier/goversioninfo v1.4.0 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/mattn/go-colorable v0.1.2 // indirect + github.com/mattn/go-isatty v0.0.8 // indirect + github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect + github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844 // indirect github.com/richardlehane/mscfb v1.0.4 // indirect github.com/richardlehane/msoleps v1.0.3 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.19.1 // indirect golang.org/x/crypto v0.8.0 // indirect + golang.org/x/image v0.10.0 // indirect golang.org/x/net v0.9.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.10.0 // indirect + golang.org/x/term v0.10.0 // indirect golang.org/x/text v0.11.0 // indirect ) diff --git a/go.sum b/go.sum index 233e81e..418bfa0 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,42 @@ +github.com/821869798/fankit v0.0.2 h1:sNxHchkG9sfgdb6+uYoD6k/yaqCuEpagGgjFmnieb5k= +github.com/821869798/fankit v0.0.2/go.mod h1:73w1EUt6i92eswVBeuUVVmHtjidOqcVsmwL3beokqQA= +github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= +github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= +github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= +github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw= +github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= +github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dchest/jsmin v0.0.0-20220218165748-59f39799265f h1:OGqDDftRTwrvUoL6pOG7rYTmWsTCvyEWFsMjg+HcOaA= +github.com/dchest/jsmin v0.0.0-20220218165748-59f39799265f/go.mod h1:Dv9D0NUlAsaQcGQZa5kc5mqR9ua72SmA8VXi4cd+cBw= +github.com/getlantern/byteexec v0.0.0-20220903141943-7db46f110fbc h1:npKLx1Gx+m6MaKnS+QVvw6wtvtf9ado42/mn8KYjD54= +github.com/getlantern/byteexec v0.0.0-20220903141943-7db46f110fbc/go.mod h1:oD9q9NB1LNBLHk3WAwza4tivxV7tm7jKFlCNCAv3+M8= +github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4= +github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= +github.com/getlantern/elevate v0.0.0-20220903142053-479ab992b264 h1:q50MSzoIIKotG7apUYaDME/bNGhOJMjG33Fpfc7KPWM= +github.com/getlantern/elevate v0.0.0-20220903142053-479ab992b264/go.mod h1:2VB8zy/kMNX347i5fdusJbPNAZE26u8qoHJDy7CWP9A= +github.com/getlantern/errors v1.0.1 h1:XukU2whlh7OdpxnkXhNH9VTLVz0EVPGKDV5K0oWhvzw= +github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A= +github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c h1:mcz27xtAkb1OuOLBct/uFfL1p3XxAIcFct82GbT+UZM= +github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c/go.mod h1:8DGAx0LNUfXNnEH+fXI0s3OCBA/351kZCiz/8YSK3i8= +github.com/getlantern/golog v0.0.0-20211223150227-d4d95a44d873 h1:nnod94N4hMKb7pyJmnXDk+HR23o1S2CbZ4oMKzHbp9A= +github.com/getlantern/golog v0.0.0-20211223150227-d4d95a44d873/go.mod h1:+ZU1h+iOVqWReBpky6d5Y2WL0sF2Llxu+QcxJFs2+OU= +github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0= +github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o= +github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 h1:XYzSdCbkzOC0FDNrgJqGRo8PCMFOBFL9py72DRs7bmc= +github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA= +github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA= +github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= github.com/gookit/goutil v0.6.12 h1:73vPUcTtVGXbhSzBOFcnSB1aJl7Jq9np3RAE50yIDZc= @@ -11,10 +45,33 @@ github.com/gookit/gsr v0.1.0 h1:0gadWaYGU4phMs0bma38t+Do5OZowRMEVlHv31p0Zig= github.com/gookit/gsr v0.1.0/go.mod h1:7wv4Y4WCnil8+DlDYHBjidzrEzfHhXEoFjEA0pPPWpI= github.com/gookit/slog v0.5.4 h1:EMctf/kap/SR8cnhkUucL0D3YZwUAJJ+WKQ/DN6kS5s= github.com/gookit/slog v0.5.4/go.mod h1:awroa12zroMvjFpS7tdpTX12AqIzVewUlC10tsj4TYY= +github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= +github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= +github.com/josephspurrier/goversioninfo v1.4.0 h1:Puhl12NSHUSALHSuzYwPYQkqa2E1+7SrtAPJorKK0C8= +github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/ncruces/zenity v0.10.10 h1:V/rtAhr5QLdDThahOkm7EYlnw4RuEsf7oN+Xb6lz1j0= +github.com/ncruces/zenity v0.10.10/go.mod h1:k3k4hJ4Wt1MUbeV48y+Gbl7Fp9skfGszN/xtKmuvhZk= +github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= +github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844 h1:GranzK4hv1/pqTIhMTXt2X8MmMOuH3hMeUR0o9SP5yc= +github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844/go.mod h1:T1TLSfyWVBRXVGzWd0o9BI4kfoO9InEgfQe4NV3mLz8= github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= @@ -22,6 +79,9 @@ github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= @@ -35,29 +95,50 @@ github.com/xuri/excelize/v2 v2.7.1 h1:gm8q0UCAyaTt3MEF5wWMjVdmthm2EHAWesGSKS9tdV github.com/xuri/excelize/v2 v2.7.1/go.mod h1:qc0+2j4TvAUrBw36ATtcTeC1VCM0fFdAXZOmcF4nTpY= github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 h1:OAmKAfT06//esDdpi/DZ8Qsdt4+M5+ltca05dA5bG2M= github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= -golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI= golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= +golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M= +golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -69,19 +150,30 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 40de2b0..ebfd6e0 100644 --- a/main.go +++ b/main.go @@ -1,12 +1,14 @@ package main import ( - "excel_merge/config" - "excel_merge/diff" - "excel_merge/merge" - "excel_merge/util" "flag" "fmt" + "github.com/821869798/excel_merge/config" + "github.com/821869798/excel_merge/diff" + "github.com/821869798/excel_merge/merge" + "github.com/821869798/excel_merge/register_tools" + "github.com/821869798/fankit/console" + "github.com/821869798/fankit/fanpath" "github.com/gookit/slog" "os" ) @@ -39,12 +41,19 @@ func main() { defer func() { if err := recover(); err != nil { slog.Errorf("[main] catch exception: %v", err) - util.AnyKeyToQuit() + console.AnyKeyToQuit() os.Exit(1) } }() - err := config.ParseConfig(*conf) + // 初始化执行目录 + err := fanpath.InitExecutePath() + if err != nil { + slog.Panicf("init execute path error:%v", err) + } + + // 加载配置文件 + err = config.ParseConfig(*conf) if err != nil { slog.Panicf("Load config toml file failed: %v", err) } else { @@ -70,6 +79,8 @@ func main() { diff.Run(fileList) } else if len(fileList) == 4 { merge.Run(fileList) + } else if len(fileList) == 0 { + register_tools.Run() } else { usage() } diff --git a/merge/merge.go b/merge/merge.go index c637f46..6df8a84 100644 --- a/merge/merge.go +++ b/merge/merge.go @@ -1,20 +1,22 @@ package merge import ( - "bufio" "errors" - "excel_merge/config" - "excel_merge/convert" - "excel_merge/define" - "excel_merge/source" - "excel_merge/util" "fmt" + "github.com/821869798/excel_merge/config" + "github.com/821869798/excel_merge/convert" + "github.com/821869798/excel_merge/define" + "github.com/821869798/excel_merge/source" + "github.com/821869798/fankit/console" + "github.com/821869798/fankit/fanpath" + "github.com/821869798/fankit/fanstr" + "github.com/AlecAivazis/survey/v2" + _ "github.com/AlecAivazis/survey/v2" "github.com/gookit/slog" "github.com/xuri/excelize/v2" "os" "os/exec" "path/filepath" - "strconv" "strings" "time" ) @@ -28,8 +30,8 @@ func Run(fileList []string) { if !define.IsExcelFile(outputFilePath) { // 非Excel文件,直接调用对比工具 slog.Infof("[merge]The merge file is not an Excel file, start comparison tools directly.") - diffArg := util.FormatFieldName(config.GlobalConfig.MergeArgs, "base", originBaseFile, "remote", originRemoteFile, "local", originLocalFile, "output", outputFilePath) - cmd := exec.Command(util.AbsOrRelExecutePath(config.GlobalConfig.CompareTools), diffArg...) + diffArg := fanstr.FormatFieldName(config.GlobalConfig.MergeArgs, "base", originBaseFile, "remote", originRemoteFile, "local", originLocalFile, "output", outputFilePath) + cmd := exec.Command(fanpath.AbsOrRelExecutePath(config.GlobalConfig.CompareTools), diffArg...) output, err := cmd.CombinedOutput() if nil != err { slog.Panicf("[merge]execute compare tool output:%s\nerror:%v", output, err) @@ -37,14 +39,14 @@ func Run(fileList []string) { return } - err := util.CreateDirIfNoExist(util.RelExecuteDir(define.WorkMergeTempDir)) + err := fanpath.CreateDirIfNoExist(fanpath.RelExecuteDir(define.WorkMergeTempDir)) if err != nil { slog.Panicf("[merge]Back local file error: %v", err) } - if util.ExistFile(outputFilePath) { - backupFile := util.RelExecuteDir(define.WorkMergeTempDir, filepath.Base(outputFilePath)) - err = util.CopyFile(outputFilePath, backupFile) + if fanpath.ExistFile(outputFilePath) { + backupFile := fanpath.RelExecuteDir(define.WorkMergeTempDir, filepath.Base(outputFilePath)) + err = fanpath.CopyFile(outputFilePath, backupFile) if err != nil { slog.Panicf("[merge]Back local file copy error: %v", err) } @@ -62,24 +64,24 @@ func Run(fileList []string) { fileName := filepath.Base(outputFilePath) tmpOutputFileName := strings.TrimSuffix(fileName, filepath.Ext(fileName)) + "." + config.GlobalConfig.MergeOutputType - tmpOutputFile := util.RelExecuteDir(define.WorkMergeTempDir, tmpOutputFileName) + tmpOutputFile := fanpath.RelExecuteDir(define.WorkMergeTempDir, tmpOutputFileName) - if util.ExistFile(tmpOutputFile) { + if fanpath.ExistFile(tmpOutputFile) { err = os.Remove(tmpOutputFile) if err != nil { slog.Panicf("[merge] remove tmp output file error: %v", err) } } - diffArg := util.FormatFieldName(config.GlobalConfig.MergeArgs, "base", baseFile, "remote", remoteFile, "local", localFile, "output", tmpOutputFile) - cmd := exec.Command(util.AbsOrRelExecutePath(config.GlobalConfig.CompareTools), diffArg...) + diffArg := fanstr.FormatFieldName(config.GlobalConfig.MergeArgs, "base", baseFile, "remote", remoteFile, "local", localFile, "output", tmpOutputFile) + cmd := exec.Command(fanpath.AbsOrRelExecutePath(config.GlobalConfig.CompareTools), diffArg...) output, err := cmd.CombinedOutput() if nil != err { slog.Panicf("[merge]execute compare tool output:%s\nerror:%v", output, err) } slog.Infof(string(output)) - selectNumber := selectBaseFile() + selectNumber := selectMergeBase() var mergeExcelFiles []string switch selectNumber { case 1: @@ -94,7 +96,7 @@ func Run(fileList []string) { return } slog.Infof("[merge] excel file complete:%s", outputFilePath) - util.AnyKeyToQuit() + console.AnyKeyToQuit() } func convertFile(file string) string { @@ -110,7 +112,7 @@ func convertFile(file string) string { outputFileName := fmt.Sprintf("%s-%d.%s", fileNameWithoutExt, timestamp, config.GlobalConfig.MergeOutputType) outputFile := filepath.Join(os.TempDir(), define.WorkGenCSVDir, outputFileName) - err = util.CreateDirIfNoExist(filepath.Dir(outputFile)) + err = fanpath.CreateDirIfNoExist(filepath.Dir(outputFile)) if err != nil { slog.Panicf("%v", err) return "" @@ -125,29 +127,19 @@ func convertFile(file string) string { return outputFile } -func selectBaseFile() int { - fmt.Printf(`Select base excel file to merge. +func selectMergeBase() int { + var answer survey.OptionAnswer + prompt := &survey.Select{ + Message: `Select base excel file to merge. The data is merged, but the formatting in the excel file of your choice is preferred. -数据是合并后的,但是优先保留你选择的excel文件中的格式。 -1. remote (基于远程分支excel结构) -2. local (基于本地分支excel结构)`) - reader := bufio.NewReader(os.Stdin) - for { - fmt.Printf("\nPlease enter your selection number(请输入你的选择数字):") - input, err := reader.ReadString('\n') - if err != nil { - fmt.Printf("input error:%v", err) - } - number, err := strconv.ParseInt(strings.TrimSpace(input), 10, 64) - if err != nil { - fmt.Printf("input error:%v", err) - } - if number > 2 || number < 1 { - fmt.Printf("input error number error!") - } else { - return int(number) - } +数据是合并后的,但是优先保留你选择的excel文件中的格式`, + Options: []string{"1. remote (基于远程分支excel结构)", "2. local (基于本地分支excel结构)"}, + } + err := survey.AskOne(prompt, &answer) + if err != nil { + slog.Panicf("[merge] select merge base excel file error: %v", err) } + return answer.Index + 1 } // mergeToExcel 将csvFile写入到excel中 diff --git a/register_tools/compare/compare.go b/register_tools/compare/compare.go new file mode 100644 index 0000000..0deda3f --- /dev/null +++ b/register_tools/compare/compare.go @@ -0,0 +1,32 @@ +package compare + +import ( + "github.com/821869798/excel_merge/define" +) + +type ICompareTools interface { + Name() string + SupportSystem() define.SystemType + GetExecuteFilePath() (string, bool) +} + +var ( + compareToolsRegister []ICompareTools +) + +func init() { + // TODO 支持更多工具 + compareToolsRegister = []ICompareTools{ + &winBeyondCompare{}, + } +} + +func SupportCompareTools(systemType define.SystemType) []ICompareTools { + var compares []ICompareTools + for _, c := range compareToolsRegister { + if c.SupportSystem() == systemType { + compares = append(compares, c) + } + } + return compares +} diff --git a/register_tools/compare/win_bc.go b/register_tools/compare/win_bc.go new file mode 100644 index 0000000..0279209 --- /dev/null +++ b/register_tools/compare/win_bc.go @@ -0,0 +1,48 @@ +package compare + +import ( + "github.com/821869798/excel_merge/define" + "github.com/821869798/excel_merge/register_tools/reg" + "golang.org/x/sys/windows/registry" + "strings" +) + +type winBeyondCompare struct { +} + +func (w *winBeyondCompare) Name() string { + return "Beyond Compare" +} + +func (w *winBeyondCompare) SupportSystem() define.SystemType { + return define.SystemTypeWindows +} + +func (w *winBeyondCompare) GetExecuteFilePath() (string, bool) { + value, err := reg.ReadRegistry(registry.LOCAL_MACHINE, `SOFTWARE\Classes\BeyondCompare.SettingsPackage\shell\open\command`, "") + if err == nil { + path, ok := parseBCPath(value) + if ok { + return path, true + } + } + return "", false +} + +func parseBCPath(str string) (string, bool) { + // 查找第一个双引号的位置 + firstQuoteIndex := strings.Index(str, "\"") + if firstQuoteIndex == -1 { + return "", false + } + + // 从第一个双引号之后查找第二个双引号的位置 + secondQuoteIndex := strings.Index(str[firstQuoteIndex+1:], "\"") + if secondQuoteIndex == -1 { + return "", false + } + + // 提取双引号中的内容 + content := str[firstQuoteIndex+1 : firstQuoteIndex+1+secondQuoteIndex] + return content, true +} diff --git a/register_tools/entry.go b/register_tools/entry.go new file mode 100644 index 0000000..4ab6c8c --- /dev/null +++ b/register_tools/entry.go @@ -0,0 +1,169 @@ +package register_tools + +import ( + "errors" + "fmt" + "github.com/821869798/excel_merge/config" + "github.com/821869798/excel_merge/define" + "github.com/821869798/excel_merge/register_tools/compare" + "github.com/821869798/excel_merge/register_tools/scm" + "github.com/821869798/fankit/admin" + "github.com/821869798/fankit/console" + "github.com/821869798/fankit/fanpath" + "github.com/AlecAivazis/survey/v2" + "github.com/gookit/slog" + "github.com/ncruces/zenity" + "os" + "runtime" +) + +var ( + registerSCMs []scm.IRegisterSCM = make([]scm.IRegisterSCM, 0) +) + +func init() { + registerSCMs = append(registerSCMs, scm.NewRegisterTortoiseGit()) + registerSCMs = append(registerSCMs, scm.NewRegisterTortoiseSVN()) + registerSCMs = append(registerSCMs, scm.NewRegisterFork()) +} + +func Run() { + var currentSystem define.SystemType = define.SystemTypeNone + switch runtime.GOOS { + case "windows": + currentSystem = define.SystemTypeWindows + if !admin.IsAdministrator() { + err := admin.StartRunAdministrator(fanpath.ExecuteFilePath(), []string{}) + if err != nil { + slog.Panicf("run as administrator error:%v", err) + } + os.Exit(200) + } + case "linux": + currentSystem = define.SystemTypeLinux + case "darwin": + currentSystem = define.SystemTypeMac + } + // 判断当前支持注册的工具 + supportRegisters := make([]scm.IRegisterSCM, 0) + for _, scm := range registerSCMs { + if scm.SupportSystem() == currentSystem { + supportRegisters = append(supportRegisters, scm) + } + } + + if len(supportRegisters) <= 0 { + slog.Panicf("[register] The current system does not support registration tools(当前系统不支持注册工具)") + } + + // 通过对话框选择对比工具 + filePath, err := SelectCompareTools(currentSystem) + if err != nil { + slog.Panicf("[register] select compare_tools error: %v", err) + return + } + + // 更新配置文件 + config.GlobalConfig.CompareTools = filePath + err = config.UpdateCompareTool(filePath) + if err != nil { + slog.Panicf("[register] Unable to update config file(无法更新配置文件):%v", err) + return + } + + // 选择需要执行的注册工具 + var options = make([]string, len(supportRegisters)) + var checked = make(map[int]bool, len(supportRegisters)) + for index, scm := range supportRegisters { + options[index] = scm.Name() + checked[index] = true + } + + // 选择注册的scm工具 + var answers = make([]survey.OptionAnswer, 0, len(supportRegisters)) + prompt := &survey.MultiSelect{ + Message: "Please select the SCM tool that needs to be registered(请选择需要注册的SCM工具,选择好后回车):", + Options: options, + Default: options, + } + err = survey.AskOne(prompt, &answers, survey.WithIcons(func(icons *survey.IconSet) { + // you can set any icons + icons.MarkedOption.Text = "[√]" + })) + if err != nil { + slog.Panicf("[register] select register_tools scm error: %v", err) + return + } + + if len(answers) == 0 { + slog.Info("[register] No SCM tool is registered(没有选择注册SCM工具)") + return + } + + // 开始注册 + for _, ans := range answers { + scm := supportRegisters[ans.Index] + ok := scm.Register(fanpath.ExecuteFilePath()) + if ok { + slog.Infof("[register] register_tools success(注册SCM工具成功): %s", scm.Name()) + } else { + slog.Infof("[register] Failed to register SCM tool, possibly not installed or open\n(注册SCM工具失败,可能是没有安装,或者是打开状态): %s", scm.Name()) + } + } + + slog.Infof("Registration completed, please reopen the version tool(注册完毕,请重新打开版本工具)") + + console.AnyKeyToQuit() +} + +// SelectCompareTools 选择对比工具 +func SelectCompareTools(currentSystem define.SystemType) (string, error) { + // 选择对比工具 + compareTools := compare.SupportCompareTools(currentSystem) + var supportToolsPath []string + var supportToolsName []string + for _, ct := range compareTools { + toolsPath, ok := ct.GetExecuteFilePath() + if ok { + supportToolsPath = append(supportToolsPath, toolsPath) + supportToolsName = append(supportToolsName, ct.Name()) + } + } + if len(supportToolsName) <= 0 { + // 直接选择自定义工具 + return SelectCustomCompareTools(currentSystem) + } + + supportToolsName = append(supportToolsName, "Select Custom(选择自定义)") + + var answer survey.OptionAnswer + prompt := &survey.Select{ + Message: `Please select a basic comparison tool(Please select a basic comparison tool):`, + Options: supportToolsName, + } + err := survey.AskOne(prompt, &answer) + if err != nil { + return "", errors.New(fmt.Sprintf("[register] select compare tools failed error: %v", err)) + } + + if answer.Index == len(supportToolsName)-1 { + return SelectCustomCompareTools(currentSystem) + } + + return supportToolsPath[answer.Index], nil +} + +// SelectCustomCompareTools 通过对话框选择对比工具 +func SelectCustomCompareTools(currentSystem define.SystemType) (string, error) { + extensions := "*.*" + if currentSystem == define.SystemTypeWindows { + extensions = "*.exe" + } + + filePath, err := zenity.SelectFile( + zenity.Title("Please select a comparison tool(请选择对比工具,例如Beyond Compare)"), + zenity.FileFilters{ + {fmt.Sprintf("execute file(%s)", extensions), []string{extensions}, true}, + }) + return filePath, err +} diff --git a/register_tools/reg/reg.go b/register_tools/reg/reg.go new file mode 100644 index 0000000..af6c565 --- /dev/null +++ b/register_tools/reg/reg.go @@ -0,0 +1,13 @@ +//go:build !windows + +package reg + +import "errors" + +func ReadRegistry(k registry.Key, path, key string) (string, error) { + return "", errors.New("not support in current system") +} + +func WriteRegistry(k registry.Key, path, key, value string) error { + return errors.New("not support in current system") +} diff --git a/register_tools/reg/reg_windows.go b/register_tools/reg/reg_windows.go new file mode 100644 index 0000000..09525e1 --- /dev/null +++ b/register_tools/reg/reg_windows.go @@ -0,0 +1,34 @@ +//go:build windows + +package reg + +import ( + "golang.org/x/sys/windows/registry" +) + +func ReadRegistry(k registry.Key, path, key string) (string, error) { + // 打开注册表项 + k, err := registry.OpenKey(k, path, registry.QUERY_VALUE) + if err != nil { + return "", err + } + defer k.Close() + + // 获取注册表键 + value, _, err := k.GetStringValue(key) + if err != nil { + return "", err + } + return value, nil +} + +func WriteRegistry(k registry.Key, path, key, value string) error { + k, err := registry.OpenKey(k, path, registry.SET_VALUE) + if err != nil { + return err + } + defer k.Close() + + err = k.SetStringValue(key, value) + return err +} diff --git a/register_tools/scm/iregister.go b/register_tools/scm/iregister.go new file mode 100644 index 0000000..7cc2ebc --- /dev/null +++ b/register_tools/scm/iregister.go @@ -0,0 +1,11 @@ +package scm + +import ( + "github.com/821869798/excel_merge/define" +) + +type IRegisterSCM interface { + Name() string + SupportSystem() define.SystemType + Register(toolPath string) bool +} diff --git a/register_tools/scm/scm_test.go b/register_tools/scm/scm_test.go new file mode 100644 index 0000000..a60f65c --- /dev/null +++ b/register_tools/scm/scm_test.go @@ -0,0 +1,16 @@ +package scm + +import ( + "os" + "testing" +) + +func TestAppData(t *testing.T) { + cacheDir, err := os.UserCacheDir() + if err != nil { + t.Errorf("无法获取用户的缓存目录:%v", err) + return + } + t.Logf("path:%v", cacheDir) + +} diff --git a/register_tools/scm/tortoise_git.go b/register_tools/scm/tortoise_git.go new file mode 100644 index 0000000..447fd90 --- /dev/null +++ b/register_tools/scm/tortoise_git.go @@ -0,0 +1,64 @@ +package scm + +import ( + "github.com/821869798/excel_merge/define" + "github.com/821869798/excel_merge/register_tools/reg" + "golang.org/x/sys/windows/registry" +) + +type RegisterTortoiseGit struct { +} + +func NewRegisterTortoiseGit() *RegisterTortoiseGit { + return &RegisterTortoiseGit{} +} + +func (r *RegisterTortoiseGit) Name() string { + return "TortoiseGit" +} + +func (r *RegisterTortoiseGit) SupportSystem() define.SystemType { + return define.SystemTypeWindows +} + +func (r *RegisterTortoiseGit) Register(toolPath string) bool { + err := reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseGit`, "Diff", toolPath) + if err != nil { + return false + } + err = reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseGit`, "Merge", toolPath) + if err != nil { + return false + } + err = reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseGit\DiffTools`, ".xlsx", toolPath) + if err != nil { + return false + } + + err = reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseGit\DiffTools`, ".xlsm", toolPath) + if err != nil { + return false + } + + err = reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseGit\DiffTools`, ".xls", toolPath) + if err != nil { + return false + } + + err = reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseGit\MergeTools`, ".xlsx", toolPath) + if err != nil { + return false + } + + err = reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseGit\MergeTools`, ".xlsm", toolPath) + if err != nil { + return false + } + + err = reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseGit\MergeTools`, ".xls", toolPath) + if err != nil { + return false + } + + return true +} diff --git a/register_tools/scm/tortoise_svn.go b/register_tools/scm/tortoise_svn.go new file mode 100644 index 0000000..1ce3067 --- /dev/null +++ b/register_tools/scm/tortoise_svn.go @@ -0,0 +1,64 @@ +package scm + +import ( + "github.com/821869798/excel_merge/define" + "github.com/821869798/excel_merge/register_tools/reg" + "golang.org/x/sys/windows/registry" +) + +type RegisterTortoiseSVN struct { +} + +func NewRegisterTortoiseSVN() *RegisterTortoiseSVN { + return &RegisterTortoiseSVN{} +} + +func (r *RegisterTortoiseSVN) Name() string { + return "TortoiseSVN" +} + +func (r *RegisterTortoiseSVN) SupportSystem() define.SystemType { + return define.SystemTypeWindows +} + +func (r *RegisterTortoiseSVN) Register(toolPath string) bool { + err := reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseSVN`, "Diff", toolPath) + if err != nil { + return false + } + err = reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseSVN`, "Merge", toolPath) + if err != nil { + return false + } + err = reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseSVN\DiffTools`, ".xlsx", toolPath) + if err != nil { + return false + } + + err = reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseSVN\DiffTools`, ".xlsm", toolPath) + if err != nil { + return false + } + + err = reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseSVN\DiffTools`, ".xls", toolPath) + if err != nil { + return false + } + + err = reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseSVN\MergeTools`, ".xlsx", toolPath) + if err != nil { + return false + } + + err = reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseSVN\MergeTools`, ".xlsm", toolPath) + if err != nil { + return false + } + + err = reg.WriteRegistry(registry.CURRENT_USER, `SOFTWARE\TortoiseSVN\MergeTools`, ".xls", toolPath) + if err != nil { + return false + } + + return true +} diff --git a/register_tools/scm/win_fork.go b/register_tools/scm/win_fork.go new file mode 100644 index 0000000..d8ac1ae --- /dev/null +++ b/register_tools/scm/win_fork.go @@ -0,0 +1,78 @@ +package scm + +import ( + "encoding/json" + "github.com/821869798/excel_merge/define" + "os" + "os/exec" + "path/filepath" +) + +type RegisterFork struct { +} + +func NewRegisterFork() *RegisterFork { + return &RegisterFork{} +} + +func (r *RegisterFork) Name() string { + return "Fork" +} + +func (r *RegisterFork) SupportSystem() define.SystemType { + return define.SystemTypeWindows +} + +func (r *RegisterFork) Register(toolPath string) bool { + localPath, err := os.UserCacheDir() + if err != nil { + return false + } + + err = killProcess("Fork.exe") + if err != nil { + return false + } + + forkSettingPath := filepath.Join(localPath, "Fork", "settings.json") + + jsonData, err := os.ReadFile(forkSettingPath) + if err != nil { + return false + } + + // 解码 JSON 数据为 map[string]interface{} + var data map[string]interface{} + err = json.Unmarshal(jsonData, &data) + if err != nil { + return false + } + + // 修改配置 + data["MergeTool"] = map[string]interface{}{ + "Type": "BeyondCompare", + "ApplicationPath": toolPath, + } + + data["ExternalDiffTool"] = map[string]interface{}{ + "Type": "BeyondCompare", + "ApplicationPath": toolPath, + } + updatedJSON, err := json.MarshalIndent(data, "", " ") + if err != nil { + return false + } + + // 将 JSON 字节切片写入回原始文件 + err = os.WriteFile(forkSettingPath, updatedJSON, 0755) + if err != nil { + return false + } + + return true +} + +func killProcess(name string) error { + cmd := exec.Command("taskkill", "/IM", name, "/F") + return cmd.Run() +} diff --git a/source/read_excel.go b/source/read_excel.go index aa5e05e..6c9bdc9 100644 --- a/source/read_excel.go +++ b/source/read_excel.go @@ -1,7 +1,7 @@ package source import ( - "excel_merge/define" + "github.com/821869798/excel_merge/define" "github.com/xuri/excelize/v2" ) diff --git a/util/console.go b/util/console.go deleted file mode 100644 index e532620..0000000 --- a/util/console.go +++ /dev/null @@ -1,12 +0,0 @@ -package util - -import ( - "fmt" - "os" -) - -func AnyKeyToQuit() { - fmt.Printf("Press any key to exit...") - b := make([]byte, 1) - _, _ = os.Stdin.Read(b) -} diff --git a/util/filepath.go b/util/filepath.go deleted file mode 100644 index 8e47556..0000000 --- a/util/filepath.go +++ /dev/null @@ -1,191 +0,0 @@ -package util - -import ( - "github.com/gookit/slog" - "io" - "os" - "path/filepath" - "regexp" - "strings" -) - -var executeDir string - -func init() { - exePath, err := os.Executable() - if err != nil { - slog.Panicf("init execute path error:%v", err) - } - executeDir = filepath.Dir(exePath) - //dir, err := filepath.Abs(filepath.Dir(os.Args[0])) - //if err != nil { - // slog.Panicf("init execute path error:%v", err) - //} - //executeDir = dir -} - -// RelExecuteDir 获取相对可执行文件所在目录 -func RelExecuteDir(paths ...string) string { - paths = append([]string{executeDir}, paths...) - return filepath.Join(paths...) -} - -// AbsOrRelExecutePath 获取绝对路径或者相对可执行文件所在目录的路径 -func AbsOrRelExecutePath(path string) string { - if filepath.IsAbs(path) { - return path - } - return RelExecuteDir(path) -} - -// SetWorkDirToExecuteDir 把工作目录设置为exe的目录 -func SetWorkDirToExecuteDir() error { - err := os.Chdir(executeDir) - return err -} - -// ExistPath 路径是否存在 -func ExistPath(path string) bool { - _, err := os.Stat(path) - return err == nil -} - -// ExistFile 文件是否存在 -func ExistFile(path string) bool { - f, err := os.Stat(path) - if err != nil { - return false - } - - return !f.IsDir() -} - -// ExistDir 文件夹是否存在 -func ExistDir(path string) bool { - f, err := os.Stat(path) - if err != nil { - return false - } - - return f.IsDir() -} - -// CreateDirIfNoExist 如果目录不存在就创建 -func CreateDirIfNoExist(path string) error { - if ExistDir(path) { - return nil - } - return os.MkdirAll(path, os.ModePerm) -} - -// GetFileListByExt 获取某个目录下ext扩展名的所有文件 -func GetFileListByExt(dir string, ext string) ([]string, error) { - var fileLists []string - err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error { - if f == nil { - return err - } - if f.IsDir() { - return nil - } - if filepath.Ext(path) == ext { - fileLists = append(fileLists, path) - } - return nil - }) - return fileLists, err -} - -// ClearDirAndCreateNew 清空目录并重新创建 -func ClearDirAndCreateNew(path string) error { - if ExistPath(path) { - err := os.RemoveAll(path) - if err != nil { - return err - } - } - err := os.MkdirAll(path, os.ModePerm) - return err -} - -// InitDirAndClearFile 初始化目录并清空目录下指定文件 -func InitDirAndClearFile(path string, removePattern string) error { - if !ExistPath(path) { - err := os.MkdirAll(path, os.ModePerm) - if err != nil { - return err - } - } - err := filepath.Walk(path, func(fileName string, f os.FileInfo, err error) error { - if ok, _ := regexp.MatchString(removePattern, fileName); !ok { - return nil - } - err = os.Remove(fileName) - return err - }) - return err -} - -// CopyFile 拷贝文件 -func CopyFile(src, dst string) error { - srcFile, err := os.Open(src) - if err != nil { - return err - } - defer srcFile.Close() - - err = CreateDirIfNoExist(filepath.Dir(dst)) - if err != nil { - return err - } - - dstFile, err := os.Create(dst) - if err != nil { - return err - } - defer dstFile.Close() - - if _, err := io.Copy(dstFile, srcFile); err != nil { - return err - } - - return dstFile.Sync() -} - -// CopyDir 拷贝目录 -func CopyDir(srcDir, dstDir string) error { - if !ExistPath(dstDir) { - err := os.MkdirAll(dstDir, os.ModePerm) - if err != nil { - return err - } - } - return filepath.Walk(srcDir, func(srcPath string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - // Ignore the source directory. - if srcPath == srcDir { - return nil - } - - baseFileName := filepath.Base(srcPath) - // Calculate the destination path. - dstPath := filepath.Join(dstDir, baseFileName) - - // Check if it's a directory, create it if so. - if info.IsDir() { - return os.MkdirAll(dstPath, info.Mode()) - } - - // It's a file, so copy it. - return CopyFile(srcPath, dstPath) - }) -} - -// GetFileNameWithoutExt 获取文件名,不带后缀 -func GetFileNameWithoutExt(pathName string) string { - fileName := filepath.Base(pathName) - return strings.TrimSuffix(fileName, filepath.Ext(fileName)) -} diff --git a/util/format.go b/util/format.go deleted file mode 100644 index daf4255..0000000 --- a/util/format.go +++ /dev/null @@ -1,37 +0,0 @@ -package util - -import ( - "fmt" - "strings" -) - -// FormatByName FormatByName("File {file} had error {error}", "file", file, "error", err) -func FormatByName(format string, args ...interface{}) string { - args2 := make([]string, len(args)) - for i, v := range args { - if i%2 == 0 { - args2[i] = fmt.Sprintf("{%v}", v) - } else { - args2[i] = fmt.Sprint(v) - } - } - r := strings.NewReplacer(args2...) - return r.Replace(format) -} - -func FormatFieldName(format string, args ...string) []string { - fields := strings.Fields(format) - formatMapping := make(map[string]string) - for i := 0; i < len(args); i += 2 { - key := "{" + args[i] + "}" - value := args[i+1] - formatMapping[key] = value - } - for i, v := range fields { - if value, ok := formatMapping[v]; ok { - fields[i] = value - } - } - - return fields -}