aboutsummaryrefslogtreecommitdiffstats
path: root/modules/util
diff options
context:
space:
mode:
authorwxiaoguang <wxiaoguang@gmail.com>2022-04-02 00:34:57 +0800
committerGitHub <noreply@github.com>2022-04-02 00:34:57 +0800
commit4f27c289472a4cfafb5a9b0e38e6a3413c30c562 (patch)
tree4a9388cfaf2efe4e9d0ed0242736769ef7e3e083 /modules/util
parent4c5cb1e2f2c7a1fcc3b151192bd45bd1bbc090bd (diff)
downloadgitea-4f27c289472a4cfafb5a9b0e38e6a3413c30c562.tar.gz
gitea-4f27c289472a4cfafb5a9b0e38e6a3413c30c562.zip
Remove legacy `unknwon/com` package (#19298)
Follows: #19284 * The `CopyDir` is only used inside test code * Rewrite `ToSnakeCase` with more test cases * The `RedisCacher` only put strings into cache, here we use internal `toStr` to replace the legacy `ToStr` * The `UniqueQueue` can use string as ID directly, no need to call `ToStr`
Diffstat (limited to 'modules/util')
-rw-r--r--modules/util/legacy.go40
-rw-r--r--modules/util/legacy_test.go33
-rw-r--r--modules/util/string.go88
-rw-r--r--modules/util/string_test.go48
4 files changed, 187 insertions, 22 deletions
diff --git a/modules/util/legacy.go b/modules/util/legacy.go
index c7da541534..d319faad09 100644
--- a/modules/util/legacy.go
+++ b/modules/util/legacy.go
@@ -9,29 +9,37 @@ import (
"crypto/cipher"
"crypto/rand"
"errors"
-
- "github.com/unknwon/com" //nolint:depguard
+ "io"
+ "os"
)
// CopyFile copies file from source to target path.
func CopyFile(src, dest string) error {
- return com.Copy(src, dest)
-}
+ si, err := os.Lstat(src)
+ if err != nil {
+ return err
+ }
-// CopyDir copy files recursively from source to target directory.
-// It returns error when error occurs in underlying functions.
-func CopyDir(srcPath, destPath string) error {
- return com.CopyDir(srcPath, destPath)
-}
+ sr, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+ defer sr.Close()
-// ToStr converts any interface to string. should be replaced.
-func ToStr(value interface{}, args ...int) string {
- return com.ToStr(value, args...)
-}
+ dw, err := os.Create(dest)
+ if err != nil {
+ return err
+ }
+ defer dw.Close()
-// ToSnakeCase converts a string to snake_case. should be replaced.
-func ToSnakeCase(str string) string {
- return com.ToSnakeCase(str)
+ if _, err = io.Copy(dw, sr); err != nil {
+ return err
+ }
+
+ if err = os.Chtimes(dest, si.ModTime(), si.ModTime()); err != nil {
+ return err
+ }
+ return os.Chmod(dest, si.Mode())
}
// AESGCMEncrypt (from legacy package): encrypts plaintext with the given key using AES in GCM mode. should be replaced.
diff --git a/modules/util/legacy_test.go b/modules/util/legacy_test.go
index cfda93d3ad..c41f7a008c 100644
--- a/modules/util/legacy_test.go
+++ b/modules/util/legacy_test.go
@@ -7,12 +7,38 @@ package util
import (
"crypto/aes"
"crypto/rand"
+ "fmt"
+ "os"
"testing"
+ "time"
"github.com/stretchr/testify/assert"
- "github.com/unknwon/com" //nolint:depguard
)
+func TestCopyFile(t *testing.T) {
+ testContent := []byte("hello")
+
+ tmpDir := os.TempDir()
+ now := time.Now()
+ srcFile := fmt.Sprintf("%s/copy-test-%d-src.txt", tmpDir, now.UnixMicro())
+ dstFile := fmt.Sprintf("%s/copy-test-%d-dst.txt", tmpDir, now.UnixMicro())
+
+ _ = os.Remove(srcFile)
+ _ = os.Remove(dstFile)
+ defer func() {
+ _ = os.Remove(srcFile)
+ _ = os.Remove(dstFile)
+ }()
+
+ err := os.WriteFile(srcFile, testContent, 0o777)
+ assert.NoError(t, err)
+ err = CopyFile(srcFile, dstFile)
+ assert.NoError(t, err)
+ dstContent, err := os.ReadFile(dstFile)
+ assert.NoError(t, err)
+ assert.Equal(t, testContent, dstContent)
+}
+
func TestAESGCM(t *testing.T) {
t.Parallel()
@@ -29,9 +55,4 @@ func TestAESGCM(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, plaintext, decrypted)
-
- // at the moment, we make sure the result is the same as the legacy package, this assertion can be removed in next round refactoring
- legacy, err := com.AESGCMDecrypt(key, ciphertext)
- assert.NoError(t, err)
- assert.Equal(t, legacy, plaintext)
}
diff --git a/modules/util/string.go b/modules/util/string.go
new file mode 100644
index 0000000000..4301f75f99
--- /dev/null
+++ b/modules/util/string.go
@@ -0,0 +1,88 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package util
+
+import "github.com/yuin/goldmark/util"
+
+func isSnakeCaseUpper(c byte) bool {
+ return 'A' <= c && c <= 'Z'
+}
+
+func isSnakeCaseLowerOrNumber(c byte) bool {
+ return 'a' <= c && c <= 'z' || '0' <= c && c <= '9'
+}
+
+// ToSnakeCase convert the input string to snake_case format.
+//
+// Some samples.
+// "FirstName" => "first_name"
+// "HTTPServer" => "http_server"
+// "NoHTTPS" => "no_https"
+// "GO_PATH" => "go_path"
+// "GO PATH" => "go_path" // space is converted to underscore.
+// "GO-PATH" => "go_path" // hyphen is converted to underscore.
+//
+func ToSnakeCase(input string) string {
+ if len(input) == 0 {
+ return ""
+ }
+
+ var res []byte
+ if len(input) == 1 {
+ c := input[0]
+ if isSnakeCaseUpper(c) {
+ res = []byte{c + 'a' - 'A'}
+ } else if isSnakeCaseLowerOrNumber(c) {
+ res = []byte{c}
+ } else {
+ res = []byte{'_'}
+ }
+ } else {
+ res = make([]byte, 0, len(input)*4/3)
+ pos := 0
+ needSep := false
+ for pos < len(input) {
+ c := input[pos]
+ if c >= 0x80 {
+ res = append(res, c)
+ pos++
+ continue
+ }
+ isUpper := isSnakeCaseUpper(c)
+ if isUpper || isSnakeCaseLowerOrNumber(c) {
+ end := pos + 1
+ if isUpper {
+ // skip the following upper letters
+ for end < len(input) && isSnakeCaseUpper(input[end]) {
+ end++
+ }
+ if end-pos > 1 && end < len(input) && isSnakeCaseLowerOrNumber(input[end]) {
+ end--
+ }
+ }
+ // skip the following lower or number letters
+ for end < len(input) && (isSnakeCaseLowerOrNumber(input[end]) || input[end] >= 0x80) {
+ end++
+ }
+ if needSep {
+ res = append(res, '_')
+ }
+ res = append(res, input[pos:end]...)
+ pos = end
+ needSep = true
+ } else {
+ res = append(res, '_')
+ pos++
+ needSep = false
+ }
+ }
+ for i := 0; i < len(res); i++ {
+ if isSnakeCaseUpper(res[i]) {
+ res[i] += 'a' - 'A'
+ }
+ }
+ }
+ return util.BytesToReadOnlyString(res)
+}
diff --git a/modules/util/string_test.go b/modules/util/string_test.go
new file mode 100644
index 0000000000..49de29ab67
--- /dev/null
+++ b/modules/util/string_test.go
@@ -0,0 +1,48 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package util
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestToSnakeCase(t *testing.T) {
+ cases := map[string]string{
+ // all old cases from the legacy package
+ "HTTPServer": "http_server",
+ "_camelCase": "_camel_case",
+ "NoHTTPS": "no_https",
+ "Wi_thF": "wi_th_f",
+ "_AnotherTES_TCaseP": "_another_tes_t_case_p",
+ "ALL": "all",
+ "_HELLO_WORLD_": "_hello_world_",
+ "HELLO_WORLD": "hello_world",
+ "HELLO____WORLD": "hello____world",
+ "TW": "tw",
+ "_C": "_c",
+
+ " sentence case ": "__sentence_case__",
+ " Mixed-hyphen case _and SENTENCE_case and UPPER-case": "_mixed_hyphen_case__and_sentence_case_and_upper_case",
+
+ // new cases
+ " ": "_",
+ "A": "a",
+ "A0": "a0",
+ "a0": "a0",
+ "Aa0": "aa0",
+ "啊": "啊",
+ "A啊": "a啊",
+ "Aa啊b": "aa啊b",
+ "A啊B": "a啊_b",
+ "Aa啊B": "aa啊_b",
+ "TheCase2": "the_case2",
+ "ObjIDs": "obj_i_ds", // the strange database column name which already exists
+ }
+ for input, expected := range cases {
+ assert.Equal(t, expected, ToSnakeCase(input))
+ }
+}