dotfiles/internal/config/config_real.go

235 lines
4.4 KiB
Go
Raw Normal View History

package config
2021-01-12 03:53:51 +00:00
import (
2023-02-25 15:30:20 +00:00
"errors"
2021-01-12 03:53:51 +00:00
"fmt"
2022-10-24 14:57:44 +00:00
"io"
"net/http"
2021-01-12 03:53:51 +00:00
"os"
"path/filepath"
2023-03-05 03:26:32 +00:00
"golang.org/x/sync/errgroup"
2021-11-19 09:44:53 +00:00
"github.com/make-go-great/copy-go"
"github.com/make-go-great/diff-go"
2021-01-12 03:53:51 +00:00
)
2023-02-25 15:30:20 +00:00
var ErrConfigInvalid = errors.New("config invalid")
2023-02-25 15:44:26 +00:00
type ConfigReal struct {
2022-10-24 14:57:44 +00:00
httpClient *http.Client
2023-02-25 15:07:37 +00:00
ConfigApps
}
2023-02-25 15:44:26 +00:00
var _ Config = (*ConfigReal)(nil)
2021-01-12 03:53:51 +00:00
// Install internal -> external
2023-02-25 15:44:26 +00:00
func (c *ConfigReal) Install() error {
2023-03-05 03:26:32 +00:00
eg := new(errgroup.Group)
for _, app := range c.Apps {
for _, p := range app.Paths {
2022-10-24 14:57:44 +00:00
if p.External == "" {
continue
}
2023-03-05 03:26:32 +00:00
p := Path{
Internal: p.Internal,
External: p.External,
URL: p.URL,
}
2023-03-05 03:26:32 +00:00
eg.Go(func() error {
if err := copy.Replace(p.Internal, p.External); err != nil {
return fmt.Errorf("copy: failed to replace [%s] -> [%s]: %w", p.Internal, p.External, err)
}
return nil
})
}
}
2023-03-05 03:26:32 +00:00
if err := eg.Wait(); err != nil {
return err
}
return nil
}
// Update external -> internal
2023-02-25 15:44:26 +00:00
func (c *ConfigReal) Update() error {
2023-03-05 03:26:32 +00:00
eg := new(errgroup.Group)
2021-01-18 08:32:25 +00:00
for _, app := range c.Apps {
for _, p := range app.Paths {
2022-10-24 14:57:44 +00:00
if p.External == "" {
continue
}
2023-03-05 03:26:32 +00:00
p := Path{
Internal: p.Internal,
External: p.External,
URL: p.URL,
2021-01-18 08:32:25 +00:00
}
2023-03-05 03:26:32 +00:00
eg.Go(func() error {
if err := copy.Replace(p.External, p.Internal); err != nil {
return fmt.Errorf("copy: failed to replace [%s] -> [%s]: %w", p.External, p.Internal, err)
}
return nil
})
2021-01-18 08:32:25 +00:00
}
}
2023-03-05 03:26:32 +00:00
if err := eg.Wait(); err != nil {
return err
}
return nil
}
2023-02-25 15:44:26 +00:00
func (c *ConfigReal) Download() error {
2023-03-05 03:26:32 +00:00
eg := new(errgroup.Group)
2022-10-24 14:57:44 +00:00
for _, app := range c.Apps {
for _, p := range app.Paths {
if p.URL == "" {
continue
}
2023-03-05 03:26:32 +00:00
p := Path{
Internal: p.Internal,
External: p.External,
URL: p.URL,
2022-10-24 14:57:44 +00:00
}
2023-03-05 03:26:32 +00:00
eg.Go(func() error {
httpRsp, err := c.httpClient.Get(p.URL)
if err != nil {
return fmt.Errorf("http client: failed to get: %w", err)
}
2022-10-24 14:57:44 +00:00
2023-03-05 03:26:32 +00:00
data, err := io.ReadAll(httpRsp.Body)
if err != nil {
return fmt.Errorf("io: failed to read all: %w", err)
}
2023-03-05 03:26:32 +00:00
// Copy from github.com/make-go-great/copy-go
// Make sure nested dir is exist before copying file
dstDir := filepath.Dir(p.Internal)
if err := os.MkdirAll(dstDir, os.ModePerm); err != nil {
return fmt.Errorf("os: failed to mkdir all [%s]: %w", dstDir, err)
}
if err := os.WriteFile(p.Internal, data, 0o600); err != nil {
return fmt.Errorf("os: failed to write file: %w", err)
}
httpRsp.Body.Close()
return nil
})
2022-10-24 14:57:44 +00:00
}
}
if err := eg.Wait(); err != nil {
return err
}
2022-10-24 14:57:44 +00:00
return nil
}
// Clean remove unused config inside config dir
2023-02-25 15:44:26 +00:00
func (c *ConfigReal) Clean() error {
unusedDirs, err := getUnusedDirs(c.Apps)
if err != nil {
return err
}
// Delete ununsed dirs to save some space
for dir := range unusedDirs {
dirPath := filepath.Join(configDirPath, dir)
if err := os.RemoveAll(dirPath); err != nil {
2023-02-25 15:36:50 +00:00
return fmt.Errorf("os: failed to remove all [%s]: %w", dir, err)
}
}
return nil
}
func getUnusedDirs(apps map[string]App) (map[string]struct{}, error) {
files, err := os.ReadDir(configDirPath)
if err != nil {
2023-02-25 15:36:50 +00:00
return nil, fmt.Errorf("os: failed to read dir [%s]: %w", configDirPath, err)
}
// Get all dirs inside config dir
unusedDirs := make(map[string]struct{})
for _, file := range files {
// Ignore config file
2023-02-25 14:55:41 +00:00
if file.Name() == configFileJSON {
continue
}
unusedDirs[file.Name()] = struct{}{}
}
// Removed used dirs
for name := range apps {
delete(unusedDirs, name)
}
return unusedDirs, nil
2021-01-12 03:53:51 +00:00
}
2023-02-25 15:30:20 +00:00
2023-02-25 15:44:26 +00:00
func (c *ConfigReal) Diff() error {
2023-02-25 15:30:20 +00:00
for _, app := range c.Apps {
for _, p := range app.Paths {
if p.External == "" {
continue
}
if err := diff.Diff(p.Internal, p.External); err != nil {
2023-02-25 15:36:50 +00:00
return fmt.Errorf("diff: failed to compare [%s] with [%s]: %w", p.Internal, p.External, err)
2023-02-25 15:30:20 +00:00
}
}
}
return nil
}
2023-02-25 15:44:26 +00:00
func (c *ConfigReal) Validate() error {
2023-03-05 03:26:32 +00:00
eg := new(errgroup.Group)
2023-02-25 15:30:20 +00:00
for _, app := range c.Apps {
for _, p := range app.Paths {
2023-03-05 03:26:32 +00:00
app := app
p := Path{
Internal: p.Internal,
External: p.External,
URL: p.URL,
2023-02-25 15:30:20 +00:00
}
2023-03-05 03:26:32 +00:00
eg.Go(func() error {
if p.Internal == "" {
return fmt.Errorf("empty internal app [%s]: %w", app, ErrConfigInvalid)
}
if p.External == "" && p.URL == "" {
return fmt.Errorf("empty external and url app [%s]: %w", app, ErrConfigInvalid)
}
return nil
})
2023-02-25 15:30:20 +00:00
}
}
2023-03-05 03:26:32 +00:00
if err := eg.Wait(); err != nil {
return err
}
2023-02-25 15:30:20 +00:00
return nil
}