mirror of
https://github.com/Theodor-Springmann-Stiftung/lenz-web.git
synced 2025-10-29 09:15:33 +00:00
BUGFIX: race condition in engine; index page controller
This commit is contained in:
11
controllers/index.go
Normal file
11
controllers/index.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Theodor-Springmann-Stiftung/lenz-web/xmlmodels"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetIndex(fiber *fiber.Ctx) error {
|
||||||
|
_ = xmlmodels.Get()
|
||||||
|
return fiber.Render("/", nil)
|
||||||
|
}
|
||||||
@@ -4,10 +4,16 @@ import (
|
|||||||
"github.com/Theodor-Springmann-Stiftung/lenz-web/helpers/middleware"
|
"github.com/Theodor-Springmann-Stiftung/lenz-web/helpers/middleware"
|
||||||
"github.com/Theodor-Springmann-Stiftung/lenz-web/server"
|
"github.com/Theodor-Springmann-Stiftung/lenz-web/server"
|
||||||
"github.com/Theodor-Springmann-Stiftung/lenz-web/views"
|
"github.com/Theodor-Springmann-Stiftung/lenz-web/views"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2/middleware/compress"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ASSETS_URL = "/assets"
|
const ASSETS_URL = "/assets"
|
||||||
|
|
||||||
func Register(server server.Server) {
|
func Register(server server.Server) {
|
||||||
|
server.Server.Use(ASSETS_URL, compress.New(compress.Config{
|
||||||
|
Level: compress.LevelBestSpeed,
|
||||||
|
}))
|
||||||
server.Server.Use(ASSETS_URL, middleware.StaticHandler(&views.StaticFS))
|
server.Server.Use(ASSETS_URL, middleware.StaticHandler(&views.StaticFS))
|
||||||
|
server.Server.Get("/", GetIndex)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,63 +1,67 @@
|
|||||||
package functions
|
package functions
|
||||||
|
|
||||||
type Month struct {
|
import (
|
||||||
Full string
|
"fmt"
|
||||||
Short string
|
"time"
|
||||||
Number string
|
)
|
||||||
No int
|
|
||||||
}
|
|
||||||
|
|
||||||
type Weekday struct {
|
type Weekday struct {
|
||||||
Full string
|
Name string
|
||||||
Short string
|
ShortName string
|
||||||
No int
|
Number int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Month) String() string {
|
type Month struct {
|
||||||
return m.Full
|
Name string
|
||||||
|
ShortName string
|
||||||
|
Number int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w Weekday) String() string {
|
var Months = []Month{
|
||||||
return w.Full
|
{"Januar", "Jan", 1},
|
||||||
|
{"Februar", "Feb", 2},
|
||||||
|
{"März", "Mär", 3},
|
||||||
|
{"April", "Apr", 4},
|
||||||
|
{"Mai", "Mai", 5},
|
||||||
|
{"Juni", "Jun", 6},
|
||||||
|
{"Juli", "Jul", 7},
|
||||||
|
{"August", "Aug", 8},
|
||||||
|
{"September", "Sep", 9},
|
||||||
|
{"Oktober", "Okt", 10},
|
||||||
|
{"November", "Nov", 11},
|
||||||
|
{"Dezember", "Dez", 12},
|
||||||
|
{"N/A", "N/A", 0},
|
||||||
}
|
}
|
||||||
|
|
||||||
var TRANSLM = []Month{
|
var Weekdays = []Weekday{
|
||||||
{"NotAvailable", "NA", "0", 0},
|
{"Sonntag", "So", 0},
|
||||||
{"Januar", "Jan", "1", 1},
|
|
||||||
{"Februar", "Feb", "2", 2},
|
|
||||||
{"März", "Mär", "3", 3},
|
|
||||||
{"April", "Apr", "4", 4},
|
|
||||||
{"Mai", "Mai", "5", 5},
|
|
||||||
{"Juni", "Jun", "6", 6},
|
|
||||||
{"Juli", "Jul", "7", 7},
|
|
||||||
{"August", "Aug", "8", 8},
|
|
||||||
{"September", "Sep", "9", 9},
|
|
||||||
{"Oktober", "Okt", "10", 10},
|
|
||||||
{"November", "Nov", "11", 11},
|
|
||||||
{"Dezember", "Dez", "12", 12},
|
|
||||||
}
|
|
||||||
|
|
||||||
var TRANSLD = []Weekday{
|
|
||||||
{"NotAvailable", "NA", 0},
|
|
||||||
{"Montag", "Mo", 1},
|
{"Montag", "Mo", 1},
|
||||||
{"Dienstag", "Di", 2},
|
{"Dienstag", "Di", 2},
|
||||||
{"Mittwoch", "Mi", 3},
|
{"Mittwoch", "Mi", 3},
|
||||||
{"Donnerstag", "Do", 4},
|
{"Donnerstag", "Do", 4},
|
||||||
{"Freitag", "Fr", 5},
|
{"Freitag", "Fr", 5},
|
||||||
{"Samstag", "Sa", 6},
|
{"Samstag", "Sa", 6},
|
||||||
{"Sonntag", "So", 7},
|
{"N/A", "N/A", 7},
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonthName(i int) Month {
|
func Today() time.Time {
|
||||||
if i > 12 || i < 1 {
|
return time.Now()
|
||||||
return TRANSLM[0]
|
|
||||||
}
|
|
||||||
return TRANSLM[i]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func WeekdayName(i int) Weekday {
|
func GetMonth(month any) Month {
|
||||||
if i > 7 || i < 1 {
|
if val, ok := month.(int); ok {
|
||||||
return TRANSLD[0]
|
val -= 1
|
||||||
|
if val < 0 || val > 11 {
|
||||||
|
val = 12
|
||||||
|
}
|
||||||
|
return Months[val]
|
||||||
}
|
}
|
||||||
return TRANSLD[i]
|
|
||||||
|
if val, ok := month.(time.Time); ok {
|
||||||
|
m := val.Month() - 1
|
||||||
|
return Months[m]
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Invalid month value", month)
|
||||||
|
return Months[12]
|
||||||
}
|
}
|
||||||
|
|||||||
6
lenz.go
6
lenz.go
@@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Theodor-Springmann-Stiftung/lenz-web/config"
|
"github.com/Theodor-Springmann-Stiftung/lenz-web/config"
|
||||||
|
"github.com/Theodor-Springmann-Stiftung/lenz-web/controllers"
|
||||||
gitprovider "github.com/Theodor-Springmann-Stiftung/lenz-web/git"
|
gitprovider "github.com/Theodor-Springmann-Stiftung/lenz-web/git"
|
||||||
"github.com/Theodor-Springmann-Stiftung/lenz-web/server"
|
"github.com/Theodor-Springmann-Stiftung/lenz-web/server"
|
||||||
"github.com/Theodor-Springmann-Stiftung/lenz-web/templating"
|
"github.com/Theodor-Springmann-Stiftung/lenz-web/templating"
|
||||||
@@ -41,7 +42,7 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// INFO: the lib, engine and storage objects passed to the server are not made to be recreated.
|
// INFO: the lib, engine and storage objects passed to the server should never be recreated.
|
||||||
err = xmlmodels.New(dir, commit.Hash)
|
err = xmlmodels.New(dir, commit.Hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -57,6 +58,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
server := server.New(engine, storage, cfg.Debug)
|
server := server.New(engine, storage, cfg.Debug)
|
||||||
|
controllers.Register(server)
|
||||||
|
|
||||||
server.Start(cfg.Address + ":" + cfg.Port)
|
server.Start(cfg.Address + ":" + cfg.Port)
|
||||||
}
|
}
|
||||||
@@ -92,11 +94,13 @@ func SetupReloadWatcher(storage fiber.Storage, engine *templating.Engine) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ResetFunction(storage fiber.Storage, engine *templating.Engine) {
|
func ResetFunction(storage fiber.Storage, engine *templating.Engine) {
|
||||||
|
slog.Debug("Resetting storage and reloading engine")
|
||||||
storage.Reset()
|
storage.Reset()
|
||||||
engine.Reload()
|
engine.Reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
func RefreshFunction(storage fiber.Storage, engine *templating.Engine) {
|
func RefreshFunction(storage fiber.Storage, engine *templating.Engine) {
|
||||||
|
slog.Debug("Resetting storage and sending refresh signal")
|
||||||
storage.Reset()
|
storage.Reset()
|
||||||
engine.Refresh()
|
engine.Refresh()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ func (e *Engine) setDebugData() {
|
|||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
defer e.mu.Unlock()
|
defer e.mu.Unlock()
|
||||||
e.debug = true
|
e.debug = true
|
||||||
e.GlobalData["debug"] = true
|
e.GlobalData["isDev"] = true
|
||||||
e.GlobalData["debugport"] = WS_SERVER
|
e.GlobalData["debugport"] = WS_SERVER
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +121,8 @@ func (e *Engine) funcs() error {
|
|||||||
|
|
||||||
// Passing HTML
|
// Passing HTML
|
||||||
e.AddFunc("Safe", functions.Safe)
|
e.AddFunc("Safe", functions.Safe)
|
||||||
|
e.AddFunc("Today", functions.Today)
|
||||||
|
e.AddFunc("GetMonth", functions.GetMonth)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -159,9 +161,9 @@ func (e *Engine) Load() error {
|
|||||||
|
|
||||||
func (e *Engine) Reload() {
|
func (e *Engine) Reload() {
|
||||||
e.regmu.Lock()
|
e.regmu.Lock()
|
||||||
defer e.regmu.Unlock()
|
|
||||||
e.LayoutRegistry = e.LayoutRegistry.Reset()
|
e.LayoutRegistry = e.LayoutRegistry.Reset()
|
||||||
e.TemplateRegistry = e.TemplateRegistry.Reset()
|
e.TemplateRegistry = e.TemplateRegistry.Reset()
|
||||||
|
e.regmu.Unlock()
|
||||||
e.Load()
|
e.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3104
views/assets/fonts/remixicon.css
Normal file
3104
views/assets/fonts/remixicon.css
Normal file
File diff suppressed because it is too large
Load Diff
BIN
views/assets/fonts/remixicon.eot
Normal file
BIN
views/assets/fonts/remixicon.eot
Normal file
Binary file not shown.
1
views/assets/fonts/remixicon.glyph.json
Normal file
1
views/assets/fonts/remixicon.glyph.json
Normal file
File diff suppressed because one or more lines are too long
3106
views/assets/fonts/remixicon.less
Normal file
3106
views/assets/fonts/remixicon.less
Normal file
File diff suppressed because it is too large
Load Diff
3088
views/assets/fonts/remixicon.module.less
Normal file
3088
views/assets/fonts/remixicon.module.less
Normal file
File diff suppressed because it is too large
Load Diff
6145
views/assets/fonts/remixicon.scss
Normal file
6145
views/assets/fonts/remixicon.scss
Normal file
File diff suppressed because it is too large
Load Diff
3075
views/assets/fonts/remixicon.styl
Normal file
3075
views/assets/fonts/remixicon.styl
Normal file
File diff suppressed because it is too large
Load Diff
9196
views/assets/fonts/remixicon.svg
Normal file
9196
views/assets/fonts/remixicon.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 2.6 MiB |
11
views/assets/fonts/remixicon.symbol.svg
Normal file
11
views/assets/fonts/remixicon.symbol.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 1.7 MiB |
BIN
views/assets/fonts/remixicon.ttf
Normal file
BIN
views/assets/fonts/remixicon.ttf
Normal file
Binary file not shown.
BIN
views/assets/fonts/remixicon.woff
Normal file
BIN
views/assets/fonts/remixicon.woff
Normal file
Binary file not shown.
BIN
views/assets/fonts/remixicon.woff2
Normal file
BIN
views/assets/fonts/remixicon.woff2
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -10,13 +10,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<span>{{- .site.title }} – ein Projekt der</span>
|
<span>{{- .title }} – ein Projekt der</span>
|
||||||
<a href="https://theodor-springmann-stiftung.de">Theodor Springmann Stiftung</a>
|
<a href="https://theodor-springmann-stiftung.de">Theodor Springmann Stiftung</a>
|
||||||
<span>·</span>
|
<span>·</span>
|
||||||
<a href="/datenschutz/">Impressum & Datenschutz</a>
|
<a href="/datenschutz/">Impressum & Datenschutz</a>
|
||||||
<span>·</span>
|
<span>·</span>
|
||||||
<i class="ri-code-line"></i>
|
<i class="ri-code-line"></i>
|
||||||
<a href="https://github.com/Theodor-Springmann-Stiftung/musenalm">Code</a>
|
<a href="https://github.com/Theodor-Springmann-Stiftung/lenz-web">Code</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
52
views/layouts/components/_globalscripts.gohtml
Normal file
52
views/layouts/components/_globalscripts.gohtml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<!-- Mark selected anchor location for CSS & Assistive Technology -->
|
||||||
|
<script type="module">
|
||||||
|
const hash = window.location.hash;
|
||||||
|
if (hash) {
|
||||||
|
const stripped = hash.slice(1);
|
||||||
|
const element = document.getElementById(stripped);
|
||||||
|
if (element) {
|
||||||
|
element.setAttribute("aria-current", "location");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{{ if .isDev }}
|
||||||
|
<!-- Reload script for development -->
|
||||||
|
<script type="module">
|
||||||
|
(function () {
|
||||||
|
let relto = -1;
|
||||||
|
const scheme = location.protocol === "https:" ? "wss" : "ws";
|
||||||
|
// Hardcode port 9000 here:
|
||||||
|
const url = scheme + "://" + location.hostname + ":9000/pb/reload";
|
||||||
|
|
||||||
|
function connect() {
|
||||||
|
const socket = new WebSocket(url);
|
||||||
|
|
||||||
|
socket.addEventListener("open", function () {
|
||||||
|
console.log("Reload socket connected (port 9000).");
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.addEventListener("message", function (evt) {
|
||||||
|
if (evt.data === "reload") {
|
||||||
|
console.log("Received reload signal. Reloading...");
|
||||||
|
if (relto !== -1) clearTimeout(relto);
|
||||||
|
relto = setTimeout(() => location.reload(), 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.addEventListener("close", function () {
|
||||||
|
console.log("Reload socket closed. Reconnecting in 3 seconds...");
|
||||||
|
setTimeout(connect, 3000);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.addEventListener("error", function (err) {
|
||||||
|
console.error("Reload socket error:", err);
|
||||||
|
// We'll let onclose handle reconnection.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiate the first connection attempt.
|
||||||
|
connect();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
{{ end }}
|
||||||
@@ -21,14 +21,8 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
|
||||||
<script src="/assets/js/alpine.min.js" defer></script>
|
|
||||||
<script src="/assets/js/htmx.min.js" defer></script>
|
|
||||||
<script src="/assets/js/htmx-response-targets.js" defer></script>
|
|
||||||
<script src="/assets/js/mark.min.js" defer></script>
|
|
||||||
|
|
||||||
<script type="module" src="/assets/scripts.js"></script>
|
<script type="module" src="/assets/scripts.js"></script>
|
||||||
<link href="/assets/css/remixicon.css" rel="stylesheet" />
|
<link href="/assets/css/remixicon.css" rel="stylesheet" />
|
||||||
<link rel="stylesheet" type="text/css" href="/assets/css/fonts.css" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="/assets/style.css" />
|
<link rel="stylesheet" type="text/css" href="/assets/style.css" />
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
@@ -67,17 +61,10 @@
|
|||||||
<!-- Default scripts... -->
|
<!-- Default scripts... -->
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
{{ block "_globalscripts" . }}
|
||||||
|
<!-- Default global scripts... -->
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
<script type="module">
|
|
||||||
const hash = window.location.hash;
|
|
||||||
if (hash) {
|
|
||||||
const stripped = hash.slice(1);
|
|
||||||
const element = document.getElementById(stripped);
|
|
||||||
if (element) {
|
|
||||||
element.setAttribute("aria-current", "location");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
3104
views/public/fonts/remixicon.css
Normal file
3104
views/public/fonts/remixicon.css
Normal file
File diff suppressed because it is too large
Load Diff
BIN
views/public/fonts/remixicon.eot
Normal file
BIN
views/public/fonts/remixicon.eot
Normal file
Binary file not shown.
1
views/public/fonts/remixicon.glyph.json
Normal file
1
views/public/fonts/remixicon.glyph.json
Normal file
File diff suppressed because one or more lines are too long
3106
views/public/fonts/remixicon.less
Normal file
3106
views/public/fonts/remixicon.less
Normal file
File diff suppressed because it is too large
Load Diff
3088
views/public/fonts/remixicon.module.less
Normal file
3088
views/public/fonts/remixicon.module.less
Normal file
File diff suppressed because it is too large
Load Diff
6145
views/public/fonts/remixicon.scss
Normal file
6145
views/public/fonts/remixicon.scss
Normal file
File diff suppressed because it is too large
Load Diff
3075
views/public/fonts/remixicon.styl
Normal file
3075
views/public/fonts/remixicon.styl
Normal file
File diff suppressed because it is too large
Load Diff
9196
views/public/fonts/remixicon.svg
Normal file
9196
views/public/fonts/remixicon.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 2.6 MiB |
11
views/public/fonts/remixicon.symbol.svg
Normal file
11
views/public/fonts/remixicon.symbol.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 1.7 MiB |
BIN
views/public/fonts/remixicon.ttf
Normal file
BIN
views/public/fonts/remixicon.ttf
Normal file
Binary file not shown.
BIN
views/public/fonts/remixicon.woff
Normal file
BIN
views/public/fonts/remixicon.woff
Normal file
Binary file not shown.
BIN
views/public/fonts/remixicon.woff2
Normal file
BIN
views/public/fonts/remixicon.woff2
Normal file
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
Helo from body!
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
// INFO: We import this so vite processes the stylesheet
|
// INFO: We import this so vite processes the stylesheet
|
||||||
|
import "../public/css/fonts.css";
|
||||||
import "./site.css";
|
import "./site.css";
|
||||||
|
import "../public/js/alpine.min.js";
|
||||||
|
import "../public/js/htmx.min.js";
|
||||||
|
|
||||||
const ATTR_XSLT_ONLOAD = "script[xslt-onload]";
|
const ATTR_XSLT_ONLOAD = "script[xslt-onload]";
|
||||||
const ATTR_XSLT_TEMPLATE = "xslt-template";
|
const ATTR_XSLT_TEMPLATE = "xslt-template";
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func New(fn func()) (*Watcher, error) {
|
|||||||
reloadTimer.Stop()
|
reloadTimer.Stop()
|
||||||
}
|
}
|
||||||
reloadTimer = time.AfterFunc(WATCHER_DEBOUNCE, func() {
|
reloadTimer = time.AfterFunc(WATCHER_DEBOUNCE, func() {
|
||||||
slog.Debug("Changes detected, reloading templates...")
|
slog.Debug("Changes detected...")
|
||||||
fn()
|
fn()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func Iterate[T any](xmlData string, initialState T) iter.Seq2[*TokenResult[T], e
|
|||||||
decoder := xml.NewDecoder(strings.NewReader(xmlData))
|
decoder := xml.NewDecoder(strings.NewReader(xmlData))
|
||||||
stack := []Element{}
|
stack := []Element{}
|
||||||
state := initialState
|
state := initialState
|
||||||
return iter.Seq2[*TokenResult[T], error](func(yield func(*TokenResult[T], error) bool) {
|
return func(yield func(*TokenResult[T], error) bool) {
|
||||||
for {
|
for {
|
||||||
token, err := decoder.Token()
|
token, err := decoder.Token()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@@ -117,7 +117,7 @@ func Iterate[T any](xmlData string, initialState T) iter.Seq2[*TokenResult[T], e
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mapAttributes converts xml.Attr to a map[string]string.
|
// mapAttributes converts xml.Attr to a map[string]string.
|
||||||
|
|||||||
@@ -134,9 +134,8 @@ func (p *XMLParser[T]) addResolvable(item T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *XMLParser[T]) ReverseLookup(item IXMLItem) []Resolved[T] {
|
func (p *XMLParser[T]) ReverseLookup(item IXMLItem) (ret []Resolved[T]) {
|
||||||
// INFO: this runs just once for the first key
|
// INFO: this runs just once for the first key
|
||||||
ret := make([]Resolved[T], 0)
|
|
||||||
keys := item.Keys()
|
keys := item.Keys()
|
||||||
|
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
@@ -146,17 +145,16 @@ func (p *XMLParser[T]) ReverseLookup(item IXMLItem) []Resolved[T] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *XMLParser[T]) String() string {
|
func (a *XMLParser[T]) String() (s string) {
|
||||||
a.RLock()
|
a.RLock()
|
||||||
defer a.RUnlock()
|
defer a.RUnlock()
|
||||||
var s string
|
|
||||||
for _, item := range a.array {
|
for _, item := range a.array {
|
||||||
s += item.String()
|
s += item.String()
|
||||||
}
|
}
|
||||||
return s
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *XMLParser[T]) Info(id string) ItemInfo {
|
func (p *XMLParser[T]) Info(id string) ItemInfo {
|
||||||
|
|||||||
Reference in New Issue
Block a user