Skip to content

Commit

Permalink
make tools.Tools struct properties unexported and use a "constructor"
Browse files Browse the repository at this point in the history
  • Loading branch information
umbynos committed Sep 29, 2023
1 parent 65728eb commit 3f05681
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 93 deletions.
17 changes: 7 additions & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,17 +180,14 @@ func loop() {
// Instantiate Index
Index = index.Init(*indexURL, config.GetDataDir())

// Instantiate Tools
Tools = tools.Tools{
Directory: config.GetDataDir().String(),
Index: Index,
Logger: func(msg string) {
mapD := map[string]string{"DownloadStatus": "Pending", "Msg": msg}
mapB, _ := json.Marshal(mapD)
h.broadcastSys <- mapB
},
logger := func(msg string) {
mapD := map[string]string{"DownloadStatus": "Pending", "Msg": msg}
mapB, _ := json.Marshal(mapD)
h.broadcastSys <- mapB
}
Tools.Init()

// Instantiate Tools
Tools = *tools.New(config.GetDataDir(), Index, logger)

// Let's handle the config
configDir := config.GetDefaultConfigDir()
Expand Down
42 changes: 18 additions & 24 deletions tools/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func pathExists(path string) bool {
// if it already exists.
func (t *Tools) Download(pack, name, version, behaviour string) error {

body, err := t.Index.Read()
body, err := t.index.Read()
if err != nil {
return err
}
Expand All @@ -90,29 +90,25 @@ func (t *Tools) Download(pack, name, version, behaviour string) error {
correctTool, correctSystem := findTool(pack, name, version, data)

if correctTool.Name == "" || correctSystem.URL == "" {
t.Logger("We couldn't find a tool with the name " + name + " and version " + version + " packaged by " + pack)
t.logger("We couldn't find a tool with the name " + name + " and version " + version + " packaged by " + pack)
return nil
}

key := correctTool.Name + "-" + correctTool.Version

// Check if it already exists
if behaviour == "keep" {
t.mutex.RLock()
location, ok := t.installed[key]
t.mutex.RUnlock()
location, ok := t.getMapValue(key)
if ok && pathExists(location) {
// overwrite the default tool with this one
t.mutex.Lock()
t.installed[correctTool.Name] = location
t.mutex.Unlock()
t.Logger("The tool is already present on the system")
t.setMapValue(correctTool.Name, location)
t.logger("The tool is already present on the system")
return t.writeMap()
}
}

// Download the tool
t.Logger("Downloading tool " + name + " from " + correctSystem.URL)
t.logger("Downloading tool " + name + " from " + correctSystem.URL)
resp, err := http.Get(correctSystem.URL)
if err != nil {
return err
Expand All @@ -134,9 +130,9 @@ func (t *Tools) Download(pack, name, version, behaviour string) error {
}

// Decompress
t.Logger("Unpacking tool " + name)
t.logger("Unpacking tool " + name)

location := path.Join(dir(), pack, correctTool.Name, correctTool.Version)
location := t.directory.Join(pack, correctTool.Name, correctTool.Version).String()
err = os.RemoveAll(location)

if err != nil {
Expand All @@ -150,18 +146,18 @@ func (t *Tools) Download(pack, name, version, behaviour string) error {

switch srcType {
case "application/zip":
location, err = extractZip(t.Logger, body, location)
location, err = extractZip(t.logger, body, location)
case "application/x-bz2":
case "application/octet-stream":
location, err = extractBz2(t.Logger, body, location)
location, err = extractBz2(t.logger, body, location)
case "application/x-gzip":
location, err = extractTarGz(t.Logger, body, location)
location, err = extractTarGz(t.logger, body, location)
default:
return errors.New("Unknown extension for file " + correctSystem.URL)
}

if err != nil {
t.Logger("Error extracting the archive: " + err.Error())
t.logger("Error extracting the archive: " + err.Error())
return err
}

Expand All @@ -171,15 +167,13 @@ func (t *Tools) Download(pack, name, version, behaviour string) error {
}

// Ensure that the files are executable
t.Logger("Ensure that the files are executable")
t.logger("Ensure that the files are executable")

// Update the tool map
t.Logger("Updating map with location " + location)
t.logger("Updating map with location " + location)

t.mutex.Lock()
t.installed[name] = location
t.installed[name+"-"+correctTool.Version] = location
t.mutex.Unlock()
t.setMapValue(name, location)
t.setMapValue(name+"-"+correctTool.Version, location)
return t.writeMap()
}

Expand Down Expand Up @@ -475,11 +469,11 @@ func (t *Tools) installDrivers(location string) error {
preamble = "./"
}
if _, err := os.Stat(filepath.Join(location, "post_install"+extension)); err == nil {
t.Logger("Installing drivers")
t.logger("Installing drivers")
ok := MessageBox("Installing drivers", "We are about to install some drivers needed to use Arduino/Genuino boards\nDo you want to continue?")
if ok == OkPressed {
os.Chdir(location)
t.Logger(preamble + "post_install" + extension)
t.logger(preamble + "post_install" + extension)
oscmd := exec.Command(preamble + "post_install" + extension)
if OS != "linux" {
// spawning a shell could be the only way to let the user type his password
Expand Down
123 changes: 64 additions & 59 deletions tools/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,49 +17,89 @@ package tools

import (
"encoding/json"
"fmt"
"os"
"os/user"
"path"
"path/filepath"
"strings"
"sync"

"github.com/arduino/arduino-create-agent/index"
"github.com/arduino/go-paths-helper"
"github.com/xrash/smetrics"
)

// Tools handle the tools necessary for an upload on a board.
// It provides a means to download a tool from the arduino servers.
//
// - *Directory* contains the location where the tools are downloaded.
// - *IndexURL* contains the url where the tools description is contained.
// - *Logger* is a StdLogger used for reporting debug and info messages
// - *installed* contains a map of the tools and their exact location
// - *directory* contains the location where the tools are downloaded.
// - *indexURL* contains the url where the tools description is contained.
// - *logger* is a StdLogger used for reporting debug and info messages
// - *installed* contains a map[string]string of the tools installed and their exact location
//
// Usage:
// You have to instantiate the struct by passing it the required parameters:
// _tools := tools.Tools{
// Directory: "/home/user/.arduino-create",
// IndexURL: "https://downloads.arduino.cc/packages/package_index.json"
// Logger: log.Logger
// }
// You have to call the New() function passing it the required parameters:
//
// index = index.Init("https://downloads.arduino.cc/packages/package_staging_index.json", dataDir)
// tools := tools.New(dataDir, index, logger)

// Tools will represent the installed tools
type Tools struct {
Directory string
Index *index.Resource
Logger func(msg string)
directory *paths.Path
index *index.Resource
logger func(msg string)
installed map[string]string
mutex sync.RWMutex
}

// Init creates the Installed map and populates it from a file in .arduino-create
func (t *Tools) Init() {
// New will return a Tool object, allowing the caller to execute operations on it.
// The New functions accept the directory to use to host the tools,
// an index (used to download the tools),
// and a logger to log the operations
func New(directory *paths.Path, index *index.Resource, logger func(msg string)) *Tools {
t := &Tools{
directory: directory,
index: index,
logger: logger,
installed: map[string]string{},
mutex: sync.RWMutex{},
}
_ = t.readMap()
return t
}

func (t *Tools) setMapValue(key, value string) {
t.mutex.Lock()
t.installed = make(map[string]string)
t.installed[key] = value
t.mutex.Unlock()
t.readMap()
}

func (t *Tools) getMapValue(key string) (string, bool) {
t.mutex.RLock()
defer t.mutex.RUnlock()
value, ok := t.installed[key]
return value, ok
}

// writeMap() writes installed map to the json file "installed.json"
func (t *Tools) writeMap() error {
t.mutex.RLock()
defer t.mutex.RUnlock()
b, err := json.Marshal(t.installed)
if err != nil {
return err
}
filePath := t.directory.Join("installed.json")
return filePath.WriteFile(b)
}

// readMap() reads the installed map from json file "installed.json"
func (t *Tools) readMap() error {
t.mutex.Lock()
defer t.mutex.Unlock()
filePath := t.directory.Join("installed.json")
b, err := filePath.ReadFile()
if err != nil {
return err
}
return json.Unmarshal(b, &t.installed)
}

// GetLocation extracts the toolname from a command like
Expand All @@ -71,22 +111,16 @@ func (t *Tools) GetLocation(command string) (string, error) {
var ok bool

// Load installed
t.mutex.RLock()
fmt.Println(t.installed)
t.mutex.RUnlock()

err := t.readMap()
if err != nil {
return "", err
}

t.mutex.RLock()
defer t.mutex.RUnlock()
fmt.Println(t.installed)

// use string similarity to resolve a runtime var with a "similar" map element
if location, ok = t.installed[command]; !ok {
if location, ok = t.getMapValue(command); !ok {
maxSimilarity := 0.0
t.mutex.RLock()
defer t.mutex.RUnlock()
for i, candidate := range t.installed {
similarity := smetrics.Jaro(command, i)
if similarity > 0.8 && similarity > maxSimilarity {
Expand All @@ -97,32 +131,3 @@ func (t *Tools) GetLocation(command string) (string, error) {
}
return filepath.ToSlash(location), nil
}

// writeMap() writes installed map to the json file "installed.json"
func (t *Tools) writeMap() error {
t.mutex.Lock()
b, err := json.Marshal(t.installed)
defer t.mutex.Unlock()
if err != nil {
return err
}
filePath := path.Join(dir(), "installed.json")
return os.WriteFile(filePath, b, 0644)
}

// readMap() reads the installed map from json file "installed.json"
func (t *Tools) readMap() error {
t.mutex.Lock()
defer t.mutex.Unlock()
filePath := path.Join(dir(), "installed.json")
b, err := os.ReadFile(filePath)
if err != nil {
return err
}
return json.Unmarshal(b, &t.installed)
}

func dir() string {
usr, _ := user.Current()
return path.Join(usr.HomeDir, ".arduino-create")
}

0 comments on commit 3f05681

Please sign in to comment.