sub/ipalloc/storage.go

148 lines
2.2 KiB
Go

package ipalloc
import (
"bufio"
"fmt"
"log"
"net/netip"
"os"
"slices"
"syscall"
)
type Store interface {
Append(Operation) error
Operations() (Scanner, error)
}
type Scanner interface {
Next() bool
Operation() Operation
Err() error
}
type Operation interface {
Prefix() netip.Prefix
}
type op struct {
prefix netip.Prefix
}
func (o op) Prefix() netip.Prefix {
return o.prefix
}
type OpProvision struct{ op }
type OpAllocation struct{ op }
type OpDeallocation struct{ op }
type Memstore struct {
ops []Operation
}
func (m *Memstore) Append(op Operation) error {
m.ops = append(m.ops, op)
return nil
}
func (m *Memstore) Operations() (Scanner, error) {
return &memscanner{opQueue: slices.Clip(m.ops)}, nil
}
type memscanner struct {
cur Operation
opQueue []Operation
}
func (m *memscanner) Next() bool {
if len(m.opQueue) == 0 {
return false
}
m.opQueue, m.cur = m.opQueue[1:], m.opQueue[0]
return true
}
func (m *memscanner) Operation() Operation {
return m.cur
}
func (m *memscanner) Err() error {
return nil
}
type Filestore struct {
filepath string
}
func (fs *Filestore) Append(op Operation) error {
f, err := os.OpenFile(fs.filepath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
return err
}
defer f.Close()
if err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX); err != nil {
return err
}
_, err = fmt.Fprintln(f, op)
return err
}
func (fs *Filestore) Operations() (Scanner, error) {
f, err := os.OpenFile(fs.filepath, os.O_RDONLY, 0)
if err != nil {
return nil, err
}
if err = syscall.Flock(int(f.Fd()), syscall.LOCK_SH); err != nil {
return nil, err
}
return newFileScanner(f), nil
}
type fileScanner struct {
err error
cur Operation
f *os.File
s bufio.Scanner
}
func newFileScanner(f *os.File) *fileScanner {
return &fileScanner{
err: nil,
cur: nil,
f: f,
s: *bufio.NewScanner(f),
}
}
func (f *fileScanner) Next() bool {
if f.f == nil {
return false
}
if !f.s.Scan() {
f.err = f.s.Err()
if err := f.f.Close(); err != nil {
log.Println(err)
}
return false
}
log.Println(f.s.Text())
f.cur = nil // TODO: parse
return true
}
func (f *fileScanner) Operation() Operation {
return f.cur
}
func (f *fileScanner) Err() error {
return f.err
}