add -p flag to dirs command

bbrodriges created

Change summary

internal/cmd/dirs.go    | 60 +++++++++++++++++++++++++++++++++++++-----
internal/config/load.go |  5 +++
2 files changed, 57 insertions(+), 8 deletions(-)

Detailed changes

internal/cmd/dirs.go 🔗

@@ -1,8 +1,10 @@
 package cmd
 
 import (
+	"fmt"
 	"os"
 	"path/filepath"
+	"strings"
 
 	"charm.land/lipgloss/v2"
 	"charm.land/lipgloss/v2/table"
@@ -17,16 +19,27 @@ var dirsCmd = &cobra.Command{
 	Long: `Print the directories where Crush stores its configuration and data files.
 This includes the global configuration directory and data directory.`,
 	Example: `
-# Print all directories
+# Print all global directories
 crush dirs
 
-# Print only the config directory
+# Print data directory and all project specific config directories
+crush dirs -p
+
+# Print only global config directory
 crush dirs config
 
+# Print only project specific config directories
+crush dirs -p config
+
 # Print only the data directory
 crush dirs data
   `,
-	Run: func(cmd *cobra.Command, args []string) {
+	RunE: func(cmd *cobra.Command, args []string) error {
+		dirs, err := configDirs(cmd)
+		if err != nil {
+			return fmt.Errorf("cannot collect config directories: %w", err)
+		}
+
 		if term.IsTerminal(os.Stdout.Fd()) {
 			// We're in a TTY: make it fancy.
 			t := table.New().
@@ -34,22 +47,29 @@ crush dirs data
 				StyleFunc(func(row, col int) lipgloss.Style {
 					return lipgloss.NewStyle().Padding(0, 2)
 				}).
-				Row("Config", filepath.Dir(config.GlobalConfig())).
+				Row("Config", dirs).
 				Row("Data", filepath.Dir(config.GlobalConfigData()))
 			lipgloss.Println(t)
-			return
+			return nil
 		}
 		// Not a TTY.
-		cmd.Println(filepath.Dir(config.GlobalConfig()))
+		cmd.Println(dirs)
 		cmd.Println(filepath.Dir(config.GlobalConfigData()))
+
+		return nil
 	},
 }
 
 var configDirCmd = &cobra.Command{
 	Use:   "config",
 	Short: "Print the configuration directory used by Crush",
-	Run: func(cmd *cobra.Command, args []string) {
-		cmd.Println(filepath.Dir(config.GlobalConfig()))
+	RunE: func(cmd *cobra.Command, args []string) error {
+		dirs, err := configDirs(cmd)
+		if err != nil {
+			return fmt.Errorf("cannot collect config directories: %w", err)
+		}
+		cmd.Println(dirs)
+		return nil
 	},
 }
 
@@ -61,6 +81,30 @@ var dataDirCmd = &cobra.Command{
 	},
 }
 
+// configDirs returns formatted string with one or more project config paths
+func configDirs(cmd *cobra.Command) (string, error) {
+	configDir := filepath.Dir(config.GlobalConfig())
+	if ok, _ := cmd.Flags().GetBool("project"); !ok {
+		return configDir, nil
+	}
+
+	cwd, err := ResolveCwd(cmd)
+	if err != nil {
+		return "", fmt.Errorf("cannot resolve current working directory: %w", err)
+	}
+
+	var sb strings.Builder
+	for i, path := range config.ProjectConfigs(cwd) {
+		if i > 0 {
+			sb.WriteByte('\n')
+		}
+		sb.WriteString(filepath.Dir(path))
+	}
+	return sb.String(), nil
+}
+
 func init() {
+	dirsCmd.PersistentFlags().BoolP("project", "p", false, "Print project specific configs")
+
 	dirsCmd.AddCommand(configDirCmd, dataDirCmd)
 }

internal/config/load.go 🔗

@@ -849,6 +849,11 @@ func GlobalCacheDir() string {
 	return filepath.Join(home.Dir(), ".cache", appName)
 }
 
+// ProjectConfigs returns list of current project configs paths.
+func ProjectConfigs(cwd string) []string {
+	return lookupConfigs(cwd)
+}
+
 // GlobalConfigData returns the path to the main data directory for the application.
 // this config is used when the app overrides configurations instead of updating the global config.
 func GlobalConfigData() string {