From 13d40ff8720bafd7b6e47540c15601cba7218486 Mon Sep 17 00:00:00 2001 From: Braeden Sowinski Date: Fri, 4 Mar 2022 13:38:47 -0800 Subject: [PATCH] First commit --- driver/localDbDriver.go | 212 ++++++++++++++++++++++++++++++++++++++++ go.mod | 8 ++ go.sum | 4 + test.go | 67 +++++++++++++ 4 files changed, 291 insertions(+) create mode 100644 driver/localDbDriver.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 test.go diff --git a/driver/localDbDriver.go b/driver/localDbDriver.go new file mode 100644 index 0000000..de8e2fc --- /dev/null +++ b/driver/localDbDriver.go @@ -0,0 +1,212 @@ +package driver + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sync" + + "github.com/google/uuid" + "github.com/jcelliott/lumber" +) + +const Version = "1.0.1" +const parentDir = "C:/local-go-db/" + +type ( + Logger interface { + Fatal(string, ...interface{}) + Error(string, ...interface{}) + Info(string, ...interface{}) + Warn(string, ...interface{}) + Debug(string, ...interface{}) + Trace(string, ...interface{}) + } + + Driver struct { + mutex sync.Mutex + mutexes map[string]*sync.Mutex + dir string + log Logger + } +) + +type Options struct { + Logger +} + +type Data struct { + ID string + Object interface{} +} + +func NewConnection(dbName string, options *Options) (*Driver, error) { + if dbName == "" { + return nil, fmt.Errorf("Cannot create Nameless database - No name given") + } + dir := parentDir + dbName + dir = filepath.Clean(dir) + + opts := Options{} + if options != nil { + opts = *options + } + + if opts.Logger == nil { + opts.Logger = lumber.NewConsoleLogger((lumber.INFO)) + } + + driver := Driver{ + dir: dir, + mutexes: make(map[string]*sync.Mutex), + log: opts.Logger, + } + + if _, err := os.Stat(dir); err == nil { + opts.Logger.Debug("Using '%s' (database already exists)\n", dir) + return &driver, nil + } + + opts.Logger.Debug("Creating the database at '%s'...\n", dir) + return &driver, os.MkdirAll(dir, 0755) +} + +func (d *Driver) InsertOne(collection string, v interface{}) error { + + if collection == "" { + return fmt.Errorf("Missing collection - no place to save record!") + } + + // Create new uuid for object and append object to database + + var data Data + data.ID = uuid.New().String() + data.Object = v + + mutex := d.getOrCreateMutex(collection) + mutex.Lock() + defer mutex.Unlock() + + dir := filepath.Join(d.dir, collection) + fnlPath := filepath.Join(dir, data.ID+".json") + tmpPath := fnlPath + ".tmp" + + if err := os.MkdirAll(dir, 0755); err != nil { + return err + } + + b, err := json.MarshalIndent(data, "", "\t") + if err != nil { + return err + } + + b = append(b, byte('\n')) + + if err := ioutil.WriteFile(tmpPath, b, 0644); err != nil { + return err + } + + return os.Rename(tmpPath, fnlPath) +} + +func (d *Driver) InsertMany(collection, resource string) { + +} + +func (d *Driver) FindOne(collection, id string) (interface{}, error) { + if collection == "" { + return nil, fmt.Errorf("Missing collection - unable to read!") + } + + record := filepath.Join(d.dir, collection, id) + + if _, err := stat(record); err != nil { + fmt.Println(err) + return nil, fmt.Errorf("The object you're searching for cannot be found") + } + + b, err := ioutil.ReadFile(record + ".json") + if err != nil { + return nil, err + } + var v Data + json.Unmarshal(b, &v) + + return v, nil +} + +func (d *Driver) FindAll(collection string) ([]string, error) { + + if collection == "" { + return nil, fmt.Errorf("Missing collection - unable to read!") + } + dir := filepath.Join(d.dir, collection) + + if _, err := stat(dir); err != nil { + return nil, err + } + + files, _ := ioutil.ReadDir(dir) + + var records []string + + for _, file := range files { + b, err := ioutil.ReadFile(filepath.Join(dir, file.Name())) + if err != nil { + return nil, err + } + + records = append(records, string(b)) + } + return records, nil +} + +func (d *Driver) Delete(collection, id string) error { + + path := filepath.Join(collection, id) + mutex := d.getOrCreateMutex(collection) + mutex.Lock() + defer mutex.Unlock() + + dir := filepath.Join(d.dir, path) + + switch fi, err := stat(dir); { + case fi == nil, err != nil: + return fmt.Errorf("Unable to find file or directory name %v\n", path) + + case fi.Mode().IsDir(): + return os.RemoveAll(dir) + + case fi.Mode().IsRegular(): + return os.RemoveAll(dir + ".json") + } + return nil +} + +func (d *Driver) getOrCreateMutex(collection string) *sync.Mutex { + + d.mutex.Lock() + defer d.mutex.Unlock() + m, ok := d.mutexes[collection] + + if !ok { + m = &sync.Mutex{} + d.mutexes[collection] = m + } + + return m +} + +func stat(path string) (fi os.FileInfo, err error) { + if fi, err = os.Stat(path); os.IsNotExist(err) { + fi, err = os.Stat(path + ".json") + } + return +} + +type User struct { + Name string + Age json.Number +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e039d54 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/SowinskiBraeden/local-go-db + +go 1.17 + +require ( + github.com/google/uuid v1.3.0 + github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..5b8da67 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25 h1:EFT6MH3igZK/dIVqgGbTqWVvkZ7wJ5iGN03SVtvvdd8= +github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25/go.mod h1:sWkGw/wsaHtRsT9zGQ/WyJCotGWG/Anow/9hsAcBWRw= diff --git a/test.go b/test.go new file mode 100644 index 0000000..fbf8938 --- /dev/null +++ b/test.go @@ -0,0 +1,67 @@ +package main + +import ( + "encoding/json" + "fmt" + + "github.com/SowinskiBraeden/local-go-db/driver" +) + +type User struct { + Name string + Age json.Number +} + +func main() { + database, err := driver.NewConnection("mytestdb", nil) + // database, err := driver.Connect("mytestdb", nil) + if err != nil { + fmt.Println("Error: ", err) + } + + // users := []User{ + // {"John Doe", "25"}, + // {"Mike Tyson", "50"}, + // } + + // for _, value := range users { + // database.InsertOne("users", User{ + // Name: value.Name, + // Age: value.Age, + // }) + // } + + // err = database.InsertOne("users", User{ + // Name: "Test User", + // Age: "19", + // }) + // if err != nil { + // fmt.Println("Error: ", err) + // } + + record, findErr := database.FindOne("users", "54d65722-1a2e-455c-a75d-38f9026e8f97") // <-- Pass in the ID of the object generated + if findErr != nil { + fmt.Println("Error: ", findErr) + } + fmt.Println(record) + + // records, err := database.ReadAll("users") + // if err != nil { + // fmt.Println("Error: ", err) + // } + // fmt.Println(records) + + // allUsers := []User{} + + // for _, f := range records { + // userFound := User{} + // if err := json.Unmarshal([]byte(f), &userFound); err != nil { + + // } + // } + + // delErr := database.Delete("users", "44516cb3-eacd-49d6-9816-30f317e2dd5d") + // if delErr != nil { + // fmt.Println("Error: ", err) + // } +}