diff --git a/cmd/commands.go b/cmd/commands.go index 6b63537..af6764c 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -21,6 +21,6 @@ var Search = &cobra.Command{ }, } -func AddCommands() { +func init() { RootCmd.AddCommand(Search) } diff --git a/cmd/root.go b/cmd/root.go index e253510..6ac89db 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,11 +1,54 @@ package cmd import ( + "log" + "strings" + "time" + + "github.com/briandowns/spinner" + ui "github.com/gizak/termui/v3" "github.com/spf13/cobra" + + h "github.com/irevenko/octotui/helpers" + tui "github.com/irevenko/octotui/tui" ) var RootCmd = &cobra.Command{ Use: "octotui", Short: "GitHub stats in your terminal", Long: `Complete documentation is available at https://github.com/irevenko/octotui`, + Run: func(cmd *cobra.Command, args []string) { + owner := h.LoadOwner() + + if owner == "" { + log.Fatalf("Owner is empty. Either add data or use the search subcommand.") + } + + nameAndType := strings.Split(owner, ":") + + if len(nameAndType) != 2 { + log.Fatalf("Default owner must be in format \"name:type\" where type is either %q or %q", h.Org, h.User) + } + + name := nameAndType[0] + ownerType := h.OwnerType(nameAndType[1]) + + s := spinner.New(spinner.CharSets[30], 100*time.Millisecond) + s.Prefix = "fetching github data " + s.FinalMSG = "done" + if err := ui.Init(); err != nil { + log.Fatalf("failed to initialize termui: %v", err) + } + defer ui.Close() + switch { + case ownerType.IsOrg(): + s.Start() + tui.RenderOrganization(name, s) + case ownerType.IsUser(): + s.Start() + tui.RenderUser(name, s) + default: + log.Fatalf("Expected either %q or %q, got %q in default_owner config file", h.Org, h.User, ownerType) + } + }, } diff --git a/helpers/default_owner.go b/helpers/default_owner.go new file mode 100644 index 0000000..befa6d8 --- /dev/null +++ b/helpers/default_owner.go @@ -0,0 +1,44 @@ +package helpers + +import ( + "fmt" + "log" +) + +const ( + ownerFilename = "default_owner" +) + +// OwnerType is the type of owner. Either "user" or "org". +type OwnerType string + +const ( + // Org signifies that the owner type is a GitHub organization. + Org OwnerType = "org" + // User signifies that the owner type is a GitHub user. + User OwnerType = "user" +) + +// LoadOwner loads the config file for the default owner. +func LoadOwner() string { + owner, filepath, err := loadConfigFile(ownerFilename) + + if err == errCreatedConfigFile { + fmt.Printf("Created owner file in: %v\n", filepath) + fmt.Println("Put your owner in this file in the format \"name:type\"") + fmt.Printf("Where type is either %q or %q\n", Org, User) + } else if err != nil { + log.Fatalf("Unable to load/create owner file: %v", err) + } + return owner +} + +// IsOrg checks if owner is a GitHub organization. +func (owner OwnerType) IsOrg() bool { + return owner == Org +} + +// IsUser checks if owner is a GitHub user. +func (owner OwnerType) IsUser() bool { + return owner == User +} diff --git a/helpers/helpers.go b/helpers/helpers.go new file mode 100644 index 0000000..386c591 --- /dev/null +++ b/helpers/helpers.go @@ -0,0 +1,53 @@ +package helpers + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "path" + "strings" +) + +var configPath = path.Join(".config", "octotui") + +var errCreatedConfigFile error = errors.New("File created") + +func loadConfigFile(filename string) (contents string, createdPath string, err error) { + home, err := os.UserHomeDir() + + if err != nil { + err = fmt.Errorf("Unable to get home directory: %w", err) + return + } + + fullConfigPath := path.Join(home, configPath) + fullFilename := path.Join(fullConfigPath, filename) + + if _, innerErr := os.Stat(fullFilename); innerErr != nil { + if os.IsNotExist(innerErr) { + innerErr := os.Mkdir(fullConfigPath, 0755) + if innerErr != nil && !os.IsExist(innerErr) { + err = fmt.Errorf("Unable to create octotui folder in %v", fullConfigPath) + return + } + + blackListFile, innerErr := os.OpenFile(fullFilename, os.O_RDONLY|os.O_CREATE, 0644) + if innerErr != nil { + err = fmt.Errorf("Unable to create file %v: %w", fullFilename, innerErr) + return + } + blackListFile.Close() + + createdPath = fullFilename + err = errCreatedConfigFile + return + } + err = innerErr + return + } + + fileContents, err := ioutil.ReadFile(fullFilename) + contents = strings.TrimSpace(string(fileContents)) + return +} diff --git a/helpers/token.go b/helpers/token.go index 97c0089..120dcea 100644 --- a/helpers/token.go +++ b/helpers/token.go @@ -2,44 +2,21 @@ package helpers import ( "fmt" - "io/ioutil" "log" - "os" - "strings" ) const ( - tokenPath = "/.config/octotui/token" + tokenFilename = "token" ) func LoadToken() string { - home, err := os.UserHomeDir() - if err != nil { - log.Fatal(err) - } - - if _, err := os.Stat(home + tokenPath); err != nil { - if os.IsNotExist(err) { - err := os.Mkdir(home+"/.config/octotui", 0755) - if err != nil { - log.Fatal("Unable to create octotui folder in " + home + "/.config") - } - - blackListFile, err := os.OpenFile(home+tokenPath, os.O_RDONLY|os.O_CREATE, 0644) - if err != nil { - log.Fatal("Unable to create token file in " + home + tokenPath) - } - blackListFile.Close() + token, filepath, err := loadConfigFile(tokenFilename) - fmt.Println("Created token file in: " + home + tokenPath) - fmt.Println("Put your github token in this file") - } + if err == errCreatedConfigFile { + fmt.Printf("Created token file in: %v\n", filepath) + fmt.Println("Put your github token in this file") + } else if err != nil { + log.Fatalf("Unable to load/create token file: %v", err) } - - token, err := ioutil.ReadFile(home + tokenPath) - if err != nil { - log.Fatal("Can't read token file in: " + home + tokenPath) - } - - return strings.TrimSpace(string(token)) + return token } diff --git a/main.go b/main.go index c5d40c7..e2ea5b2 100644 --- a/main.go +++ b/main.go @@ -8,8 +8,6 @@ import ( ) func main() { - cmd.AddCommands() - if err := cmd.RootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) diff --git a/tui/render_stats.go b/tui/render_stats.go index 0258113..8d325c0 100644 --- a/tui/render_stats.go +++ b/tui/render_stats.go @@ -27,15 +27,15 @@ func RenderStats(username string, accType string, s *spinner.Spinner) { defer ui.Close() if accType == "(user)" { - renderUser(username, s) + RenderUser(username, s) } if accType == "(organization)" { - renderOrganization(username, s) + RenderOrganization(username, s) } } -func renderUser(username string, s *spinner.Spinner) { +func RenderUser(username string, s *spinner.Spinner) { user, err := g.UserDetails(qlClient, username) if err != nil { log.Fatalf("Couldn't get user details for: %v: %v", username, err) @@ -80,7 +80,7 @@ func renderUser(username string, s *spinner.Spinner) { } } -func renderOrganization(username string, s *spinner.Spinner) { +func RenderOrganization(username string, s *spinner.Spinner) { org, err := g.OrganizationDetails(qlClient, username) if err != nil { log.Fatalf("Couldn't get org details for: %v: %v", username, err)