diff --git a/internal/command/command.go b/internal/command/command.go index e2549a8..ec4ef59 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -106,14 +106,14 @@ func parsePages(root int, parentMapping map[int][]database.Item) (database.Apps, for _, item := range parentMapping[page.ID] { switch item.Type { case database.ApplicationType: - utils.Indent(log.WithField("title", item.App.Title).Info)("found app") + utils.Indent(log.WithField("title", item.App.Title).Info, 2)("found app") p.Items = append(p.Items, item.App.Title) // case database.WidgetType: // utils.Indent(log.WithField("title", item.Widget.Title).Info)("found widget") // p.Items = append(p.Items, item.Widget.Title) case database.FolderRootType: - utils.Indent(log.WithField("title", item.Group.Title).Info)("found folder") + utils.Indent(log.WithField("title", item.Group.Title).Info, 2)("found folder") f := database.AppFolder{Name: item.Group.Title} @@ -122,12 +122,12 @@ func parsePages(root int, parentMapping map[int][]database.Item) (database.Apps, } for fpIndex, fpage := range parentMapping[item.ID] { - utils.DoubleIndent(log.WithField("number", fpIndex+1).Info)("found folder page") + utils.Indent(log.WithField("number", fpIndex+1).Info, 3)("found folder page") fp := database.FolderPage{Number: fpIndex + 1} for _, folder := range parentMapping[fpage.ID] { - utils.TripleIndent(log.WithField("title", folder.App.Title).Info)("found app") + utils.Indent(log.WithField("title", folder.App.Title).Info, 4)("found app") fp.Items = append(fp.Items, folder.App.Title) } @@ -137,13 +137,13 @@ func parsePages(root int, parentMapping map[int][]database.Item) (database.Apps, if len(f.Pages) > 0 && len(f.Pages[0].Items) > 0 { p.Items = append(p.Items, f) } else { - utils.DoubleIndent(log.WithField("folder", item.Group.Title).Error)("empty folder") + utils.Indent(log.WithField("folder", item.Group.Title).Error, 3)("empty folder") } case database.PageType: - utils.Indent(log.WithField("parent_id", item.ParentID).Info)("found page") + utils.Indent(log.WithField("parent_id", item.ParentID).Info, 2)("found page") default: - utils.Indent(log.WithField("type", item.Type).Error)("found ?") + utils.Indent(log.WithField("type", item.Type).Error, 2)("found ?") } } apps.Pages = append(apps.Pages, p) @@ -163,9 +163,9 @@ func DefaultOrg(c *Config) (err error) { lpad.File = filepath.Join(lpad.Folder, "db") // lpad.File = "./launchpad.db" if _, err := os.Stat(lpad.File); os.IsNotExist(err) { - utils.Indent(log.WithError(err).WithField("path", lpad.File).Fatal)("launchpad DB not found") + utils.Indent(log.WithError(err).WithField("path", lpad.File).Fatal, 2)("launchpad DB not found") } - utils.Indent(log.WithFields(log.Fields{"database": lpad.File}).Info)("found launchpad database") + utils.Indent(log.WithFields(log.Fields{"database": lpad.File}).Info, 2)("found launchpad database") // start from a clean slate err = removeOldDatabaseFiles(lpad.Folder) @@ -210,7 +210,7 @@ func DefaultOrg(c *Config) (err error) { // groupID := int(math.Max(float64(lpad.GetMaxAppID()), float64(lpad.GetMaxWidgetID()))) groupID := int(float64(lpad.GetMaxAppID())) // widgets are no longer supported - utils.Indent(log.Info)("creating folders out of app categories") + utils.Indent(log.Info, 2)("creating folders out of app categories") // Create default config file var apps []database.App @@ -227,12 +227,12 @@ func DefaultOrg(c *Config) (err error) { folderName := strings.Title(strings.Replace(strings.TrimPrefix(category.UTI, "public.app-category."), "-", " ", 1)) folder := database.AppFolder{Name: folderName} folderPage := database.FolderPage{Number: 1} - utils.DoubleIndent(log.WithField("folder", folderName).Info)("adding folder") + utils.Indent(log.WithField("folder", folderName).Info, 3)("adding folder") if err := lpad.DB.Where("category_id = ?", category.ID).Find(&apps).Error; err != nil { log.WithError(err).Error("categories query failed") } for _, app := range apps { - utils.TripleIndent(log.WithField("app", app.Title).Info)("adding app to category folder") + utils.Indent(log.WithField("app", app.Title).Info, 4)("adding app to category folder") folderPage.Items = utils.AppendIfMissing(folderPage.Items, app.Title) } folder.Pages = append(folder.Pages, folderPage) @@ -261,7 +261,7 @@ func DefaultOrg(c *Config) (err error) { return fmt.Errorf("failed to GetMissing=>Apps: %v", err) } - utils.Indent(log.Info)("creating App folders and adding apps to them") + utils.Indent(log.Info, 2)("creating App folders and adding apps to them") if err := lpad.ApplyConfig(config.Apps, groupID, 1); err != nil { return fmt.Errorf("failed to DefaultOrg->ApplyConfig: %w", err) } @@ -298,9 +298,9 @@ func SaveConfig(c *Config) (err error) { lpad.File = filepath.Join(lpad.Folder, "db") // lpad.File = "./launchpad.db" if _, err := os.Stat(lpad.File); os.IsNotExist(err) { - utils.Indent(log.WithError(err).WithField("path", lpad.File).Fatal)("launchpad DB not found") + utils.Indent(log.WithError(err).WithField("path", lpad.File).Fatal, 2)("launchpad DB not found") } - utils.Indent(log.WithFields(log.Fields{"database": lpad.File}).Info)("found launchpad database") + utils.Indent(log.WithFields(log.Fields{"database": lpad.File}).Info, 2)("found launchpad database") // open launchpad database lpad.DB, err = gorm.Open(sqlite.Open(lpad.File), &gorm.Config{ @@ -448,9 +448,9 @@ func LoadConfig(c *Config) (err error) { lpad.File = filepath.Join(lpad.Folder, "db") // lpad.File = "./launchpad-test.db" if _, err := os.Stat(lpad.File); os.IsNotExist(err) { - utils.Indent(log.WithError(err).WithField("path", lpad.File).Fatal)("launchpad DB not found") + utils.Indent(log.WithError(err).WithField("path", lpad.File).Fatal, 2)("launchpad DB not found") } - utils.Indent(log.WithFields(log.Fields{"database": lpad.File}).Info)("found launchpad database") + utils.Indent(log.WithFields(log.Fields{"database": lpad.File}).Info, 2)("found launchpad database") // start from a clean slate err = removeOldDatabaseFiles(lpad.Folder) @@ -515,7 +515,11 @@ func LoadConfig(c *Config) (err error) { return fmt.Errorf("failed to GetMissing=>Apps: %v", err) } - utils.Indent(log.Info)("creating App folders and adding apps to them") + if err := lpad.Config.Verify(); err != nil { + return fmt.Errorf("failed to verify conf post removal of missing apps: %v", err) + } + + utils.Indent(log.Info, 2)("creating App folders and adding apps to them") if err := lpad.ApplyConfig(lpad.Config.Apps, groupID, 1); err != nil { return fmt.Errorf("failed to LoadConfig->ApplyConfig: %w", err) } @@ -534,12 +538,12 @@ func LoadConfig(c *Config) (err error) { } if len(lpad.Config.Desktop.Image) > 0 { - utils.Indent(log.WithField("image", lpad.Config.Desktop.Image).Info)("setting desktop background image") + utils.Indent(log.WithField("image", lpad.Config.Desktop.Image).Info, 2)("setting desktop background image") desktop.SetDesktopImage(lpad.Config.Desktop.Image) } if len(lpad.Config.Dock.Apps) > 0 || len(lpad.Config.Dock.Others) > 0 { - utils.Indent(log.Info)("setting dock apps") + utils.Indent(log.Info, 2)("setting dock apps") dPlist, err := dock.LoadDockPlist() if err != nil { return errors.Wrap(err, "unable to load dock plist") @@ -548,14 +552,14 @@ func LoadConfig(c *Config) (err error) { dPlist.PersistentApps = nil // remove all apps from dock } for _, app := range lpad.Config.Dock.Apps { - utils.DoubleIndent(log.WithField("app", app).Info)("adding to dock") + utils.Indent(log.WithField("app", app).Info, 3)("adding to dock") dPlist.AddApp(app) } if len(dPlist.PersistentOthers) > 0 { dPlist.PersistentOthers = nil // remove all folders from dock } for _, other := range lpad.Config.Dock.Others { - utils.DoubleIndent(log.WithField("other", other).Info)("adding to dock") + utils.Indent(log.WithField("other", other).Info, 3)("adding to dock") dPlist.AddOther(other) } if lpad.Config.Dock.Settings != nil { diff --git a/internal/command/utils.go b/internal/command/utils.go index 5d99830..a09a8ff 100644 --- a/internal/command/utils.go +++ b/internal/command/utils.go @@ -70,7 +70,7 @@ var PorgASCIIArt = ` ` func restartDock() error { - utils.Indent(log.Info)("restarting Dock") + utils.Indent(log.Info, 2)("restarting Dock") if _, err := utils.RunCommand(context.Background(), "killall", "Dock"); err != nil { return errors.Wrap(err, "killing Dock process failed") } @@ -89,13 +89,13 @@ func removeOldDatabaseFiles(dbpath string) error { for _, path := range paths { if _, err := os.Stat(path); os.IsNotExist(err) { - utils.DoubleIndent(log.WithField("path", path).Warn)("DB file not found") + utils.Indent(log.WithField("path", path).Warn, 3)("DB file not found") continue } if err := os.Remove(path); err != nil { return errors.Wrap(err, "removing file failed") } - utils.DoubleIndent(log.WithField("path", path).Info)("removed old DB file") + utils.Indent(log.WithField("path", path).Info, 3)("removed old DB file") } return restartDock() diff --git a/internal/database/config.go b/internal/database/config.go index 4ac5608..6988ad8 100644 --- a/internal/database/config.go +++ b/internal/database/config.go @@ -44,6 +44,29 @@ func (c Config) GetFolderContainingApp(app string) (string, error) { return "", fmt.Errorf("unable to find folder containing app %s", app) } +// Verify that the config is valid +func (c Config) Verify() error { + for _, page := range c.Apps.Pages { + for _, item := range page.Items { + switch item.(type) { + case string: + continue + default: + var folder AppFolder + if err := mapstructure.Decode(item, &folder); err != nil { + return fmt.Errorf("mapstructure unable to decode config folder") + } + if len(folder.Pages) > 0 { + if len(folder.Pages[0].Items) < 2 { // verify that all folders contain more than 1 item + return fmt.Errorf("folder %s must contain more than 1 item to be valid", folder.Name) + } + } + } + } + } + return nil +} + // Apps is the launchpad apps config object type Apps struct { Pages []Page `yaml:"pages" json:"pages,omitempty"` @@ -128,37 +151,21 @@ type Dock struct { func LoadConfig(filename string) (Config, error) { var conf Config - utils.Indent(log.WithField("path", filename).Info)("parsing launchpad config YAML") + utils.Indent(log.WithField("path", filename).Info, 2)("parsing launchpad config YAML") data, err := os.ReadFile(filename) if err != nil { - utils.DoubleIndent(log.WithError(err).WithField("path", filename).Fatal)("config file not found") + utils.Indent(log.WithError(err).WithField("path", filename).Fatal, 3)("config file not found") return conf, err } err = yaml.Unmarshal(data, &conf) if err != nil { - utils.DoubleIndent(log.WithError(err).WithField("path", filename).Fatal)("unmarshalling yaml failed") + utils.Indent(log.WithError(err).WithField("path", filename).Fatal, 3)("unmarshalling yaml failed") return conf, err } - // verify that all folders contain more than 1 item - for _, page := range conf.Apps.Pages { - for _, item := range page.Items { - switch item.(type) { - case string: - continue - default: - var folder AppFolder - if err := mapstructure.Decode(item, &folder); err != nil { - return Config{}, errors.Wrap(err, "mapstructure unable to decode config folder") - } - if len(folder.Pages) > 0 { - if len(folder.Pages[0].Items) < 2 { - return Config{}, fmt.Errorf("folder %s must contain more than 1 item to be valid", folder.Name) - } - } - } - } + if err := conf.Verify(); err != nil { + return conf, fmt.Errorf("config verification failed: %v", err) } return conf, nil diff --git a/internal/database/database.go b/internal/database/database.go index 852a539..31deec3 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -63,7 +63,7 @@ func (lp *LaunchPad) GetMissing(apps *Apps, appType int) error { for _, app := range lp.dbApps { if !slices.Contains(lp.confApps, app) { - utils.DoubleIndent(log.WithField("app", app).Warn)("found installed apps that are not in supplied config") + utils.Indent(log.WithField("app", app).Warn, 3)("found installed apps that are not in supplied config") if len(apps.Pages[len(apps.Pages)-1].Items) < 35 { apps.Pages[len(apps.Pages)-1].Items = append(apps.Pages[len(apps.Pages)-1].Items, app) } else { @@ -79,11 +79,11 @@ func (lp *LaunchPad) GetMissing(apps *Apps, appType int) error { // check all apps from config file exist on system for idx, page := range apps.Pages { tmp := []any{} - for iidx, item := range page.Items { + for _, item := range page.Items { switch item.(type) { case string: if !slices.Contains(lp.dbApps, item.(string)) { - utils.DoubleIndent(log.WithField("app", item.(string)).Warn)("found app in config that are is not on system") + utils.Indent(log.WithField("app", item.(string)).Warn, 3)("found app in config that are is not on system") } else { tmp = append(tmp, item) } @@ -96,13 +96,14 @@ func (lp *LaunchPad) GetMissing(apps *Apps, appType int) error { ftmp := []any{} for _, fitem := range fpage.Items { if !slices.Contains(lp.dbApps, fitem) { - utils.DoubleIndent(log.WithField("app", fitem).Warn)("found app in config that are is not on system") + utils.Indent(log.WithField("app", fitem).Warn, 3)("found app in config that are is not on system") } else { ftmp = append(ftmp, fitem) } } - apps.Pages[idx].Items[iidx].(map[string]any)["pages"].([]any)[fpIdx].(map[string]any)["items"] = ftmp + item.(map[string]any)["pages"].([]any)[fpIdx].(map[string]any)["items"] = ftmp } + tmp = append(tmp, item) } } apps.Pages[idx].Items = tmp @@ -113,7 +114,7 @@ func (lp *LaunchPad) GetMissing(apps *Apps, appType int) error { // ClearGroups clears out items related to groups func (lp *LaunchPad) ClearGroups() error { - utils.Indent(log.Info)("clear out groups") + utils.Indent(log.Info, 2)("clear out groups") var items []Item if err := lp.DB.Where("type in (?)", []int{RootType, FolderRootType, PageType}).Delete(&items).Error; err != nil { return fmt.Errorf("delete items associted with groups failed: %w", err) @@ -132,7 +133,7 @@ func (lp *LaunchPad) FlattenApps() error { lp.DisableTriggers() - utils.Indent(log.Info)("flattening out apps") + utils.Indent(log.Info, 2)("flattening out apps") for idx, app := range apps { if err := lp.updateItem(app.Title, ApplicationType, lp.rootPage, idx); err != nil { return fmt.Errorf("failed to update app '%s': %w", app.Title, err) @@ -156,7 +157,7 @@ func (lp *LaunchPad) AddRootsAndHoldingPages() error { {ID: 6, UUID: "HOLDINGPAGE_VERS", Type: PageType, ParentID: 5, Ordering: 0}, } - utils.Indent(log.Info)("add root and holding pages") + utils.Indent(log.Info, 2)("add root and holding pages") for _, item := range items { if err := lp.DB.Create(&item).Error; err != nil { return errors.Wrap(err, "db insert item failed") @@ -185,7 +186,7 @@ func (lp *LaunchPad) createNewPage(rowID, pageParentID, pageNumber int) error { return fmt.Errorf("failed to create page item with ID=%d: %w", rowID, err) } - utils.DoubleIndent(log.WithField("number", pageNumber).Info)("page added") + utils.Indent(log.WithField("number", pageNumber).Info, 3)("page added") if err := lp.DB.Create(&Group{ID: rowID}).Error; err != nil { return fmt.Errorf("failed to create group for page with ID=%d: %w", rowID, err) } @@ -213,7 +214,7 @@ func (lp *LaunchPad) createNewFolder(folderName string, rowID, folderParentID, f return fmt.Errorf("failed to create folder '%s' item with ID=%d: %w", folderName, rowID, err) } - utils.DoubleIndent(log.WithField("group", folderName).Info)("folder added") + utils.Indent(log.WithField("group", folderName).Info, 4)("folder added") if err := lp.DB.Create(&Group{ ID: rowID, Title: folderName, @@ -239,7 +240,7 @@ func (lp *LaunchPad) createNewFolderPage(rowID, folderPageParentID, folderPageNu if err := lp.DB.Create(&item).Error; err != nil { return fmt.Errorf("failed to create folder page item with ID=%d: %w", rowID, err) } - utils.DoubleIndent(log.WithField("number", folderPageNumber).Info)("folder page added") + utils.Indent(log.WithField("number", folderPageNumber).Info, 5)("folder page added") if err := lp.DB.Create(&Group{ID: rowID}).Error; err != nil { return fmt.Errorf("failed to create group for folder page with ID=%d: %w", rowID, err) } @@ -265,7 +266,7 @@ func (lp *LaunchPad) updateItem(item string, itemType, parentID, ordering int) e lp.DB.Model(&i).Association("App").Find(&i.App) case WidgetType: if result := lp.DB.Where("title = ?", item).First(&w); result.Error != nil && errors.Is(result.Error, gorm.ErrRecordNotFound) { - utils.DoubleIndent(log.WithField("app", item).Warn)("widget not installed. SKIPPING...") + utils.Indent(log.WithField("app", item).Warn, 3)("widget not installed. SKIPPING...") return nil } if err := lp.DB.Where("rowid = ?", w.ID).First(&i).Error; err != nil { @@ -490,7 +491,7 @@ func (lp *LaunchPad) FixOther() error { // move apps to root page for _, app := range apps { - utils.DoubleIndent(log.WithField("app", app.Title).Warn)("moving app from Other folder") + utils.Indent(log.WithField("app", app.Title).Warn, 3)("moving app from Other folder") if cfolder, err := lp.Config.GetFolderContainingApp(app.Title); err == nil { if err := lp.addToFolder(app.Title, cfolder); err != nil { // add to folder it SHOULD have been in return err @@ -524,7 +525,7 @@ func (lp *LaunchPad) FixOther() error { // EnableTriggers enables item update triggers func (lp *LaunchPad) EnableTriggers() error { - utils.Indent(log.Info)("enabling SQL update triggers") + utils.Indent(log.Info, 2)("enabling SQL update triggers") if err := lp.DB.Exec("UPDATE dbinfo SET value=0 WHERE key='ignore_items_update_triggers';").Error; err != nil { return errors.Wrap(err, "counld not update `ignore_items_update_triggers` to 0") } @@ -533,7 +534,7 @@ func (lp *LaunchPad) EnableTriggers() error { // DisableTriggers disables item update triggers func (lp *LaunchPad) DisableTriggers() error { - utils.Indent(log.Info)("disabling SQL update triggers") + utils.Indent(log.Info, 2)("disabling SQL update triggers") if err := lp.DB.Exec("UPDATE dbinfo SET value=1 WHERE key='ignore_items_update_triggers';").Error; err != nil { return errors.Wrap(err, "counld not update `ignore_items_update_triggers` to 1") } @@ -557,7 +558,7 @@ func (lp *LaunchPad) GetMaxAppID() int { var apps []App if err := lp.DB.Find(&apps).Error; err != nil { - utils.Indent(log.WithError(err).Error)("query all apps failed") + utils.Indent(log.WithError(err).Error, 2)("query all apps failed") } maxID := 0 @@ -576,7 +577,7 @@ func (lp *LaunchPad) GetMaxWidgetID() int { var widgets []Widget if err := lp.DB.Find(&widgets).Error; err != nil { - utils.Indent(log.WithError(err).Error)("query all widgets failed") + utils.Indent(log.WithError(err).Error, 2)("query all widgets failed") } maxID := 0 diff --git a/internal/dock/dock.go b/internal/dock/dock.go index 4684820..36cc69d 100644 --- a/internal/dock/dock.go +++ b/internal/dock/dock.go @@ -277,7 +277,7 @@ func (p *Plist) Save() error { if err != nil { return fmt.Errorf("failed to create temp file: %v", err) } - utils.DoubleIndent(log.WithField("plist", tmp.Name()).Info)("writing temp dock plist") + utils.Indent(log.WithField("plist", tmp.Name()).Info, 3)("writing temp dock plist") if err := plist.NewBinaryEncoder(tmp).Encode(p); err != nil { return fmt.Errorf("failed to decode plist: %w", err) } @@ -291,7 +291,7 @@ func (p *Plist) Save() error { } func (p *Plist) importPlist(path string) error { - utils.DoubleIndent(log.Info)("importing dock plist") + utils.Indent(log.Info, 3)("importing dock plist") if _, err := utils.RunCommand(context.Background(), "/usr/bin/defaults", "import", "com.apple.dock", path); err != nil { return fmt.Errorf("failed to defaults import dock plist '%s': %v", path, err) } @@ -299,7 +299,7 @@ func (p *Plist) importPlist(path string) error { } func (p *Plist) kickstart() error { - utils.DoubleIndent(log.Info)("restarting com.apple.Dock.agent service") + utils.Indent(log.Info, 3)("restarting com.apple.Dock.agent service") if _, err := utils.RunCommand(context.Background(), "/bin/launchctl", "kickstart", "-k", fmt.Sprintf("gui/%d/com.apple.Dock.agent", os.Getuid())); err != nil { return fmt.Errorf("failed to kickstart dock: %v", err) } diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 370e832..aa01ae1 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -10,34 +10,13 @@ import ( ) var ( - normalPadding = cli.Default.Padding - doublePadding = normalPadding * 2 - triplePadding = normalPadding * 3 - quadruplePadding = normalPadding * 4 + normalPadding = cli.Default.Padding ) -// Indent indents apex log line -func Indent(f func(s string)) func(string) { +// Indent indents apex log line to supplied level +func Indent(f func(s string), level int) func(string) { return func(s string) { - cli.Default.Padding = doublePadding - f(s) - cli.Default.Padding = normalPadding - } -} - -// DoubleIndent double indents apex log line -func DoubleIndent(f func(s string)) func(string) { - return func(s string) { - cli.Default.Padding = triplePadding - f(s) - cli.Default.Padding = normalPadding - } -} - -// TripleIndent triple indents apex log line -func TripleIndent(f func(s string)) func(string) { - return func(s string) { - cli.Default.Padding = quadruplePadding + cli.Default.Padding = normalPadding * level f(s) cli.Default.Padding = normalPadding }