package protocoldb import ( "fmt" "io/fs" "log" "os" "os/user" "path" "path/filepath" "github.com/go-git/go-git/v5" "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() ([]DatabaseEntry, 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 Update(prot *protocol.ProtocolStructure) 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 } // Init initializes the databases, opens the default one and checks for others if available func Init() *PPFDatabases { // initialize main list databases := PPFDatabases{[]ProtocolDatabaseProvider{}} // initialize default db user, err := user.Current() if err != nil { log.Printf("Current user not obtainable: %s", err) log.Fatal("Impossible to open default database") } pathlist := []string{user.HomeDir, ".config", "ppforge"} fdb := FileDatabase{path.Join(pathlist...), map[string]DatabaseEntry{}, false} fdb.Open(path.Join(pathlist...)) fdb.UpdateDatabase() databases.Databases = append(databases.Databases, fdb) log.Printf("Amount of databases available %d", len(databases.Databases)) return &databases } // Get Retrieves the full set of databases func (ppfdb *PPFDatabases) Get() []ProtocolDatabaseProvider { return ppfdb.Databases } // 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 gitRepo git.Repository } // 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() ([]DatabaseEntry, error) { entries := []DatabaseEntry{} 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 entry := DatabaseEntry{path, prot.Metadata} fd.protocols[prot.Metadata.Name] = entry entries = append(entries, entry) } } return nil }) if err != nil { return nil, err } } if entries == nil || len(entries) == 0 { entries = make([]DatabaseEntry, len(fd.protocols)) for _, v := range fd.protocols { entries = append(entries, v) } } return entries, 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 } // 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) } // 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 }