mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-06-01 04:12:10 +00:00
Use a general approach to access custom/static/builtin assets (#24022)
The idea is to use a Layered Asset File-system (modules/assetfs/layered.go) For example: when there are 2 layers: "custom", "builtin", when access to asset "my/page.tmpl", the Layered Asset File-system will first try to use "custom" assets, if not found, then use "builtin" assets. This approach will hugely simplify a lot of code, make them testable. Other changes: * Simplify the AssetsHandlerFunc code * Simplify the `gitea embedded` sub-command code --------- Co-authored-by: Jason Song <i@wolfogre.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
parent
42919ccb7c
commit
50a72e7a83
36 changed files with 689 additions and 1055 deletions
|
@ -74,29 +74,28 @@ const pathSeparator = string(os.PathSeparator)
|
|||
//
|
||||
// {`/foo`, ``, `bar`} => `/foo/bar`
|
||||
// {`/foo`, `..`, `bar`} => `/foo/bar`
|
||||
func FilePathJoinAbs(elem ...string) string {
|
||||
elems := make([]string, len(elem))
|
||||
func FilePathJoinAbs(base string, sub ...string) string {
|
||||
elems := make([]string, 1, len(sub)+1)
|
||||
|
||||
// POISX filesystem can have `\` in file names. Windows: `\` and `/` are both used for path separators
|
||||
// POSIX filesystem can have `\` in file names. Windows: `\` and `/` are both used for path separators
|
||||
// to keep the behavior consistent, we do not allow `\` in file names, replace all `\` with `/`
|
||||
if isOSWindows() {
|
||||
elems[0] = filepath.Clean(elem[0])
|
||||
elems[0] = filepath.Clean(base)
|
||||
} else {
|
||||
elems[0] = filepath.Clean(strings.ReplaceAll(elem[0], "\\", pathSeparator))
|
||||
elems[0] = filepath.Clean(strings.ReplaceAll(base, "\\", pathSeparator))
|
||||
}
|
||||
if !filepath.IsAbs(elems[0]) {
|
||||
// This shouldn't happen. If there is really necessary to pass in relative path, return the full path with filepath.Abs() instead
|
||||
panic(fmt.Sprintf("FilePathJoinAbs: %q (for path %v) is not absolute, do not guess a relative path based on current working directory", elems[0], elems))
|
||||
}
|
||||
|
||||
for i := 1; i < len(elem); i++ {
|
||||
if elem[i] == "" {
|
||||
for _, s := range sub {
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
if isOSWindows() {
|
||||
elems[i] = filepath.Clean(pathSeparator + elem[i])
|
||||
elems = append(elems, filepath.Clean(pathSeparator+s))
|
||||
} else {
|
||||
elems[i] = filepath.Clean(pathSeparator + strings.ReplaceAll(elem[i], "\\", pathSeparator))
|
||||
elems = append(elems, filepath.Clean(pathSeparator+strings.ReplaceAll(s, "\\", pathSeparator)))
|
||||
}
|
||||
}
|
||||
// the elems[0] must be an absolute path, just join them together
|
||||
|
|
|
@ -207,6 +207,6 @@ func TestCleanPath(t *testing.T) {
|
|||
}
|
||||
}
|
||||
for _, c := range cases {
|
||||
assert.Equal(t, c.expected, FilePathJoinAbs(c.elems...), "case: %v", c.elems)
|
||||
assert.Equal(t, c.expected, FilePathJoinAbs(c.elems[0], c.elems[1:]...), "case: %v", c.elems)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -18,3 +19,30 @@ func StopTimer(t *time.Timer) bool {
|
|||
}
|
||||
return stopped
|
||||
}
|
||||
|
||||
func Debounce(d time.Duration) func(f func()) {
|
||||
type debouncer struct {
|
||||
mu sync.Mutex
|
||||
t *time.Timer
|
||||
}
|
||||
db := &debouncer{}
|
||||
|
||||
return func(f func()) {
|
||||
db.mu.Lock()
|
||||
defer db.mu.Unlock()
|
||||
|
||||
if db.t != nil {
|
||||
db.t.Stop()
|
||||
}
|
||||
var trigger *time.Timer
|
||||
trigger = time.AfterFunc(d, func() {
|
||||
db.mu.Lock()
|
||||
defer db.mu.Unlock()
|
||||
if trigger == db.t {
|
||||
f()
|
||||
db.t = nil
|
||||
}
|
||||
})
|
||||
db.t = trigger
|
||||
}
|
||||
}
|
||||
|
|
30
modules/util/timer_test.go
Normal file
30
modules/util/timer_test.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDebounce(t *testing.T) {
|
||||
var c int64
|
||||
d := Debounce(50 * time.Millisecond)
|
||||
d(func() { atomic.AddInt64(&c, 1) })
|
||||
assert.EqualValues(t, 0, atomic.LoadInt64(&c))
|
||||
d(func() { atomic.AddInt64(&c, 1) })
|
||||
d(func() { atomic.AddInt64(&c, 1) })
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
assert.EqualValues(t, 1, atomic.LoadInt64(&c))
|
||||
d(func() { atomic.AddInt64(&c, 1) })
|
||||
assert.EqualValues(t, 1, atomic.LoadInt64(&c))
|
||||
d(func() { atomic.AddInt64(&c, 1) })
|
||||
d(func() { atomic.AddInt64(&c, 1) })
|
||||
d(func() { atomic.AddInt64(&c, 1) })
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
assert.EqualValues(t, 2, atomic.LoadInt64(&c))
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue