package protocoldb import ( "fmt" "io/fs" "os" "path" "path/filepath" "strings" "gitea.mmo.to/ProtocolPacketForger/ppf/protocol" "gitea.mmo.to/ProtocolPacketForger/ppf/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 }