ppforge/protocoldb/protocoldb.go

170 lines
4.9 KiB
Go

package protocoldb
import (
"fmt"
"io/fs"
"os"
"path"
"path/filepath"
"strings"
"gitea.mmo.to/ppForge/ppforge/protocol"
"gitea.mmo.to/ppForge/ppforge/protocolctl"
)
// PPFDatabases is a list of all databases, with the most simple interface
type PPFDatabases struct {
Databases []ProtocolDatabaseProvider
}
// ProtocolDatabaseProvider is a simple protocol database interface, which only provides open/close and retrieving the contents
type ProtocolDatabaseProvider interface {
// Open opens a new database
Open(path string) error
// Close closes a database (only internal flag set)
Close() error
// Protocols returns the content of the database as map, an error if the first read of the database failed
Protocols() (map[string]protocol.DOPMeta, error)
}
// ProtocolDatabaseManager is a complex protocol database interface, which allows adding, updating and refreshing of the content
type ProtocolDatabaseManager interface {
Add(prot *protocol.ProtocolStructure) error
AddFile(path string) error
Update(prot *protocol.ProtocolStructure) error
UpdateFile(path string) error
// no remove.
UpdateDatabase() error // scans for new protocols
}
// DatabaseEntry is a single entry in the file database, additionally to the meta data the path is required
type DatabaseEntry struct {
Path string
Protocol protocol.DOPMeta
}
// Get a full protocol from a database entry
func (de *DatabaseEntry) Get() (*protocol.ProtocolStructure, error) {
return protocolctl.LoadNew(de.Path)
}
// FileDatabase implements ProtocolDatabaseProvider and ProtocolDatabaseManager
type FileDatabase struct {
Path string
protocols map[string]DatabaseEntry
closed bool
}
// FileDatabaseFromGitRemote implements retrieving the latest stuff from an external git remote additionally to the FileDatabase functionality
// TODO
type FileDatabaseFromGitRemote struct {
FileDatabase
RemoteGitRepository string
}
// Open the database
func (fd *FileDatabase) Open(path string) error {
fileinfo, err := os.Stat(path)
if err == os.ErrNotExist {
return err
}
if !fileinfo.IsDir() {
return fmt.Errorf("Path %s is not a directory", path)
}
fd.Path = path
fd.closed = false
return nil
}
// Close the database and free the data
func (fd *FileDatabase) Close() error {
for k := range fd.protocols {
delete(fd.protocols, k)
}
fd.closed = true
return nil
}
// Protocols returns the map of protocols or an error if the first read failed
func (fd *FileDatabase) Protocols() (map[string]DatabaseEntry, error) {
if !fd.closed && len(fd.protocols) == 0 {
fd.protocols = map[string]DatabaseEntry{}
// recurse recursively through the path
err := filepath.Walk(fd.Path, func(path string, info fs.FileInfo, err error) error {
if !info.IsDir() {
prot, err := protocolctl.LoadNew(path)
if err == nil {
// add to map
fd.protocols[prot.Metadata.Name] = DatabaseEntry{path, prot.Metadata}
}
}
return nil
})
if err != nil {
return nil, err
}
}
return fd.protocols, nil
}
// Add puts a new protocol into the path of the file database
func (fd *FileDatabase) Add(prot *protocol.ProtocolStructure) error {
p := path.Join(fd.Path, prot.Metadata.Name, ".protocoljson")
err := protocolctl.Save(prot, p)
if err == nil {
fd.protocols[prot.Metadata.Name] = DatabaseEntry{p, prot.Metadata}
}
return err
}
// AddFile copies a external protocoljson file into the database directory and updates the internal data
func (fd *FileDatabase) AddFile(p string) error {
prot, err := protocolctl.LoadNew(p)
if err == nil {
if !strings.HasPrefix(p, fd.Path) {
// add it to the database if not existing there
fd.Add(prot)
}
p = path.Join(fd.Path, prot.Metadata.Name, ".protocoljson")
fd.protocols[prot.Metadata.Name] = DatabaseEntry{p, prot.Metadata}
}
return err
}
// Update updates an existing entry through saving the contents of the protocol to the file, or just adding it anew
func (fd *FileDatabase) Update(prot *protocol.ProtocolStructure) error {
if v, ok := fd.protocols[prot.Metadata.Name]; ok {
v.Protocol = prot.Metadata
return protocolctl.Save(prot, v.Path)
}
return fd.Add(prot)
}
// UpdateFile simply adds a file if it is outside the database, or updates the internal storage with the contents of the file
func (fd *FileDatabase) UpdateFile(p string) error {
if !strings.HasPrefix(p, fd.Path) {
return fd.AddFile(p)
}
prot, err := protocolctl.LoadNew(p)
if err == nil {
fd.protocols[prot.Metadata.Name] = DatabaseEntry{p, prot.Metadata}
}
return err
}
// UpdateDatabase just rereads all files
func (fd *FileDatabase) UpdateDatabase() error {
// recurse recursively through the path
err := filepath.Walk(fd.Path, func(path string, info fs.FileInfo, err error) error {
if !info.IsDir() {
prot, err := protocolctl.LoadNew(path)
if err == nil {
// add to map
fd.protocols[prot.Metadata.Name] = DatabaseEntry{path, prot.Metadata}
}
}
return nil
})
return err
}