github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/builtins/core/modules/cmd-list.go (about)

     1  package modules
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  
     9  	"github.com/lmorg/murex/config/profile"
    10  	"github.com/lmorg/murex/debug"
    11  	"github.com/lmorg/murex/lang"
    12  	"github.com/lmorg/murex/lang/types"
    13  )
    14  
    15  const expecting = `Expecting enabled | disabled | loaded | not-loaded | packages`
    16  
    17  func listModules(p *lang.Process) error {
    18  	p.Stdout.SetDataType(types.Json)
    19  
    20  	flag, _ := p.Parameters.String(1)
    21  
    22  	switch flag {
    23  	case "enabled":
    24  		return listAndPrint(listModulesEnDis, p, true)
    25  
    26  	case "disabled":
    27  		return listAndPrint(listModulesEnDis, p, false)
    28  
    29  	case "loaded":
    30  		return listAndPrint(listModulesLoadNotLoad, p, true)
    31  
    32  	case "not-loaded":
    33  		return listAndPrint(listModulesLoadNotLoad, p, false)
    34  
    35  	case "packages":
    36  		return listPackages(p)
    37  
    38  	case "":
    39  		return fmt.Errorf("missing parameter. %s", expecting)
    40  
    41  	default:
    42  		return fmt.Errorf("invalid parameter `%s`. %s", flag, expecting)
    43  	}
    44  }
    45  
    46  // listAndPrint is a wrapper function around the listModules...() functions. The
    47  // rational behind this weird design is so that the code is concise and readable
    48  // for normal execution but easily testable (ie the p.Stdout.Write code) is
    49  // removed from the logic
    50  func listAndPrint(fn func(*lang.Process, bool) (map[string]string, error), p *lang.Process, enabled bool) error {
    51  	list, err := fn(p, enabled)
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	b, err := lang.MarshalData(p, types.Json, &list)
    57  	if err != nil {
    58  		return err
    59  	}
    60  
    61  	_, err = p.Stdout.Write(b)
    62  	return err
    63  }
    64  
    65  // listModulesEnDis reads from disk rather than the package cache (like `runtime`)
    66  // because the typical use for `murex-package list enabled|disabled` is to view
    67  // which packages and modules will be loaded with murex. To get a view of what is
    68  // currently loaded in a given session then use `loaded` / `not-loaded` instead of
    69  // `enabled` / `disabled`
    70  func listModulesEnDis(p *lang.Process, enabled bool) (map[string]string, error) {
    71  	var disabled []string
    72  	modulePath := profile.ModulePath()
    73  
    74  	err := profile.ReadJson(modulePath+profile.DisabledFile, &disabled)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	isDisabled := func(name string) bool {
    80  		for i := range disabled {
    81  			if disabled[i] == name {
    82  				return true
    83  			}
    84  		}
    85  
    86  		return false
    87  	}
    88  
    89  	paths, err := filepath.Glob(modulePath + "*")
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	list := make(map[string]string)
    95  
    96  	for _, pack := range paths {
    97  		f, err := os.Stat(pack)
    98  		if err != nil {
    99  			return nil, err
   100  		}
   101  
   102  		// only read directories
   103  		if !f.IsDir() {
   104  			debug.Log("File not directory:", pack)
   105  			continue
   106  		}
   107  
   108  		// no hidden files nor empty strings
   109  		if len(f.Name()) == 0 || f.Name()[0] == '.' {
   110  			continue
   111  		}
   112  
   113  		mods, err := profile.LoadPackage(pack, false)
   114  		if err != nil {
   115  			write(p, "{RED}%s{RESET}", err.Error())
   116  		}
   117  
   118  		// these should NOT equate ;)
   119  		if strings.HasSuffix(f.Name(), profile.IgnoredExt) != enabled {
   120  			name := cropIgnoreExt(f.Name())
   121  			list[name] = name
   122  		}
   123  
   124  		for i := range mods {
   125  			if isDisabled(mods[i].Package+"/"+mods[i].Name) == enabled {
   126  				continue
   127  			}
   128  			list[mods[i].Package+"/"+mods[i].Name] = mods[i].Summary
   129  		}
   130  	}
   131  
   132  	return list, nil
   133  }
   134  
   135  func listModulesLoadNotLoad(p *lang.Process, loaded bool) (map[string]string, error) {
   136  	list := make(map[string]string)
   137  
   138  	for _, mods := range profile.Packages {
   139  		for i := range mods {
   140  			if mods[i].Loaded == loaded {
   141  				list[mods[i].Package+"/"+mods[i].Name] = mods[i].Summary
   142  			}
   143  		}
   144  	}
   145  
   146  	return list, nil
   147  }
   148  
   149  func listPackages(p *lang.Process) error {
   150  	paths, err := filepath.Glob(profile.ModulePath() + "*")
   151  	if err != nil {
   152  		return err
   153  	}
   154  
   155  	var list []string
   156  
   157  	for _, pack := range paths {
   158  		f, err := os.Stat(pack)
   159  		if err != nil {
   160  			return err
   161  		}
   162  		if !f.IsDir() {
   163  			debug.Log("File not directory:", pack)
   164  			continue
   165  		}
   166  
   167  		list = append(list, cropIgnoreExt(f.Name()))
   168  	}
   169  
   170  	b, err := lang.MarshalData(p, types.Json, &list)
   171  	if err != nil {
   172  		return err
   173  	}
   174  	_, err = p.Stdout.Write(b)
   175  	return err
   176  }
   177  
   178  func cropIgnoreExt(name string) string {
   179  	return strings.Replace(name, profile.IgnoredExt, "", 1)
   180  }