package mindnum
import (
mindnumDomain "github.com/yanosea/mindnum/v2/app/domain/mindnum"
)
// GetMindnumUseCase is a struct that provides the use case of getting a mind number.
type GetMindnumUseCase struct {
mindnumRepo mindnumDomain.MindnumRepository
}
// NewGetMindnumUseCase returns a new instance of the GetMindnumUseCase struct.
func NewGetMindnumUseCase(
mindnumRepo mindnumDomain.MindnumRepository,
) *GetMindnumUseCase {
return &GetMindnumUseCase{
mindnumRepo: mindnumRepo,
}
}
// GetMindnumUsecaseOutputDto is a struct that provides the output DTO of the GetMindnumUseCase struct.
type GetMindnumUsecaseOutputDto struct {
MindNumber int
Description string
}
// Run returns the output of the GetMindnumUseCase struct.
func (uc *GetMindnumUseCase) Run(birthday string) (*GetMindnumUsecaseOutputDto, error) {
mindNumber, err := mindnumDomain.GetMindNumber(birthday)
if err != nil {
return nil, err
}
mindnum, err := uc.mindnumRepo.FindByNumber(mindNumber)
if err != nil {
return nil, err
}
return &GetMindnumUsecaseOutputDto{
MindNumber: mindnum.MindNumber,
Description: mindnum.Description,
}, nil
}
package mindnum
import ()
// VersionUseCase is a struct that contains the use case of the version.
type VersionUseCase struct{}
// NewVersionUseCase returns a new instance of the VersionUseCase struct.
func NewVersionUseCase() *VersionUseCase {
return &VersionUseCase{}
}
// VersionUsecaseOutputDto is a DTO struct that contains the output data of the VersionUseCase.
type VersionUsecaseOutputDto struct {
Version string
}
// Run returns the output of the VersionUseCase.
func (uc *VersionUseCase) Run(version string) *VersionUsecaseOutputDto {
return &VersionUsecaseOutputDto{
Version: version,
}
}
package mindnum
import (
"errors"
"strconv"
"strings"
"time"
)
// Mindnum is a struct that represents a mind number and its description.
type Mindnum struct {
MindNumber int
Description string
}
// NewMindnum returns a new instance of the Mindnum struct.
func NewMindnum(mindNumber int, description string) (*Mindnum, error) {
if mindNumber < 1 || 9 < mindNumber {
return nil, errors.New("mind number out of range")
}
return &Mindnum{
MindNumber: mindNumber,
Description: description,
}, nil
}
// GetMindNumber returns a mind number from a birthday.
func GetMindNumber(birthday string) (int, error) {
if birthday == "" {
return 0, errors.New("birthday is required")
}
if _, err := time.Parse("20060102", birthday); err != nil {
return 0, errors.New("invalid date format")
}
var mindNum int
tmpNum := birthday
for {
tmpSlice := strings.Split(tmpNum, "")
for _, v := range tmpSlice {
n, _ := strconv.Atoi(v)
mindNum += int(n)
}
if mindNum < 10 {
break
} else {
tmpNum = strconv.Itoa(mindNum)
mindNum = 0
}
}
return mindNum, nil
}
package repository
import (
"errors"
mindnumDomain "github.com/yanosea/mindnum/v2/app/domain/mindnum"
)
// mindnumRepository is a struct that implements the MindnumRepository interface.
type mindnumRepository struct{}
// NewMindnumRepository returns a new instance of the mindnumRepository struct.
func NewMindnumRepository() mindnumDomain.MindnumRepository {
return &mindnumRepository{}
}
// FindByNumber returns a mindnum by number.
func (r *mindnumRepository) FindByNumber(number int) (*mindnumDomain.Mindnum, error) {
var description string
switch number {
case 1:
description = DescriptionOne
case 2:
description = DescriptionTwo
case 3:
description = DescriptionThree
case 4:
description = DescriptionFour
case 5:
description = DescriptionFive
case 6:
description = DescriptionSix
case 7:
description = DescriptionSeven
case 8:
description = DescriptionEight
case 9:
description = DescriptionNine
default:
return nil, errors.New("number out of range")
}
return mindnumDomain.NewMindnum(number, description)
}
package command
import (
"os"
"github.com/yanosea/mindnum/v2/app/presentation/cli/mindnum/formatter"
"github.com/yanosea/mindnum/v2/app/presentation/cli/mindnum/presenter"
"github.com/yanosea/mindnum/v2/pkg/proxy"
)
// Cli is a struct that represents the command line interface of mindnum cli.
type Cli struct {
Cobra proxy.Cobra
Version string
RootCommand proxy.Command
}
var (
// output is the output string
output string
// format is the format of the output
format = "text"
)
// NewCli returns a new instance of the Cli struct.
func NewCli(
cobra proxy.Cobra,
version string,
) *Cli {
rootCommand := NewRootCommand(cobra, version, &format, &output)
return &Cli{
Cobra: cobra,
Version: version,
RootCommand: rootCommand,
}
}
// Run runs the command line interface of mindnum cli.
func (c *Cli) Run() int {
out := os.Stdout
exitCode := 0
if err := c.RootCommand.Execute(); err != nil {
output = formatter.AppendErrorToOutput(err, output)
out = os.Stderr
exitCode = 1
}
if err := presenter.Print(out, output); err != nil {
exitCode = 1
}
return exitCode
}
package mindnum
import (
c "github.com/spf13/cobra"
mindnumApp "github.com/yanosea/mindnum/v2/app/application/mindnum"
mindnumRepo "github.com/yanosea/mindnum/v2/app/infrastructure/text/repository"
"github.com/yanosea/mindnum/v2/app/presentation/cli/mindnum/formatter"
"github.com/yanosea/mindnum/v2/pkg/proxy"
)
var (
// birthday is the receiver of the birthday flag
birthday string
)
// NewGetCommand returns a new instance of the get command.
func NewGetCommand(
cobra proxy.Cobra,
format *string,
output *string,
) proxy.Command {
cmd := cobra.NewCommand()
cmd.SetSilenceErrors(true)
cmd.SetUse("get [birthday (optional)]")
cmd.SetAliases([]string{"gt", "g"})
cmd.SetUsageTemplate(getUsageTemplate)
cmd.SetHelpTemplate(getHelpTemplate)
cmd.PersistentFlags().StringVarP(
&birthday,
"birthday",
"b",
"",
"Birthday in the format 'yyyyMMdd' (e.g. 20000101) (optional)",
)
cmd.SetRunE(
func(_ *c.Command, args []string) error {
return runGet(&birthday, format, output, args)
},
)
return cmd
}
// runGet runs the get command.
func runGet(birthday *string, format *string, output *string, args []string) error {
if len(args) > 0 {
*birthday = args[0]
}
mindnumRepo := mindnumRepo.NewMindnumRepository()
uc := mindnumApp.NewGetMindnumUseCase(mindnumRepo)
dto, err := uc.Run(*birthday)
if err != nil {
return err
}
f, err := formatter.NewFormatter(*format)
if err != nil {
return err
}
o := f.Format(dto)
*output = o
return nil
}
const (
// getHelpTemplate is the help template of the get command.
getHelpTemplate = `🧠 Get a mind number from an argument or the birthday flag and show the personality description
` + getUsageTemplate
// getUsageTemplate is the usage template of the get command.
getUsageTemplate = `Usage:
mindnum get [flags] [argument]
Flags:
-b, --birthday string 🎂 birthday in the format 'yyyyMMdd' (e.g. 19900719) (optional)
-h, --help 🤝 help for mindnum get
Arguments:
birthday 🎂 birthday in the format 'yyyyMMdd' (e.g. 19900719) (optional)
`
)
package mindnum
import (
c "github.com/spf13/cobra"
mindnumApp "github.com/yanosea/mindnum/v2/app/application/mindnum"
"github.com/yanosea/mindnum/v2/app/presentation/cli/mindnum/formatter"
"github.com/yanosea/mindnum/v2/pkg/proxy"
)
// NewVersionCommand returns a new instance of the version command.
func NewVersionCommand(
cobra proxy.Cobra,
version string,
format *string,
output *string,
) proxy.Command {
cmd := cobra.NewCommand()
cmd.SetSilenceErrors(true)
cmd.SetUse("version")
cmd.SetUsageTemplate(versionUsageTemplate)
cmd.SetHelpTemplate(versionHelpTemplate)
cmd.SetArgs(cobra.ExactArgs(0))
cmd.SetRunE(
func(_ *c.Command, _ []string) error {
return runVersion(version, format, output)
},
)
return cmd
}
// runVersion runs the version command.
func runVersion(version string, format *string, output *string) error {
uc := mindnumApp.NewVersionUseCase()
dto := uc.Run(version)
f, err := formatter.NewFormatter(*format)
if err != nil {
return err
}
o := f.Format(dto)
*output = o
return nil
}
const (
// versionHelpTemplate is the help template of the version command.
versionHelpTemplate = `🔖 Show the version of mindnum
` + versionUsageTemplate
// versionUsageTemplate is the usage template of the version command.
versionUsageTemplate = `Usage:
mindnum version [flags]
Flags:
-h, --help 🤝 help for mindnum version
`
)
package command
import (
"github.com/yanosea/mindnum/v2/app/presentation/cli/mindnum/command/mindnum"
"github.com/yanosea/mindnum/v2/pkg/proxy"
)
// NewRootCommand returns a new instance of the root command.
func NewRootCommand(
cobra proxy.Cobra,
version string,
format *string,
output *string,
) proxy.Command {
cmd := cobra.NewCommand()
cmd.SetSilenceErrors(true)
cmd.SetUse("mindnum")
cmd.SetUsageTemplate(rootUsageTemplate)
cmd.SetHelpTemplate(rootHelpTemplate)
cmd.SetVersion(version)
cmd.AddCommand(
mindnum.NewVersionCommand(
cobra,
version,
format,
output,
),
mindnum.NewGetCommand(
cobra,
format,
output,
),
)
return cmd
}
const (
// rootHelpTemplate is the help template of the root command.
rootHelpTemplate = `🧠 mindnum is a CLI tool to get the mind number from the birthday
` + rootUsageTemplate
// rootUsageTemplate is the usage template of the root command.
rootUsageTemplate = `Usage:
mindnum [command]
Available Commands:
get 🧠 Get a mind number from an argument or the birthday flag and show the personality description
help 🤝 Help about any command
completion 🔧 Generate the autocompletion script for the specified shell
version 🔖 Show the version of mindnum
Flags:
-h, --help 🤝 help for mindnum
-v, --version 🔖 version for mindnum
Use "mindnum [command] --help" for more information about a command.
`
)
package formatter
import (
"errors"
"fmt"
)
// Formatter is an interface that formats the output of mindnum cli.
type Formatter interface {
Format(result interface{}) string
}
// NewFormatter returns a new instance of the Formatter interface.
func NewFormatter(
format string,
) (Formatter, error) {
var f Formatter
switch format {
case "text":
f = NewTextFormatter()
default:
return nil, errors.New("invalid format")
}
return f, nil
}
// AppendErrorToOutput appends an error to the output.
func AppendErrorToOutput(err error, output string) string {
if err == nil && output == "" {
return ""
}
var result string
if err != nil {
if output == "" {
result = fmt.Sprintf("Error : %s", err)
} else {
result = fmt.Sprintf("Error : %s\n%s", err, output)
}
} else {
result = output
}
if result != "" {
result = Red(result)
}
return result
}
package formatter
import (
"fmt"
"strings"
mindnumApp "github.com/yanosea/mindnum/v2/app/application/mindnum"
)
// TextFormatter is a struct that formats the output of mindnum cli.
type TextFormatter struct{}
// NewTextFormatter returns a new instance of the TextFormatter struct.
func NewTextFormatter() *TextFormatter {
return &TextFormatter{}
}
// Format formats the output of mindnum cli.
func (f *TextFormatter) Format(result interface{}) string {
var formatted string
switch v := result.(type) {
case *mindnumApp.VersionUsecaseOutputDto:
formatted = fmt.Sprintf("mindnum version %s", v.Version)
case *mindnumApp.GetMindnumUsecaseOutputDto:
var builder strings.Builder
builder.WriteString(fmt.Sprintf("Your mind number is %d!", v.MindNumber))
if trimmedDescription := strings.TrimSpace(v.Description); trimmedDescription != "" {
builder.WriteString("\n\n")
builder.WriteString(trimmedDescription)
}
formatted = builder.String()
}
return formatted
}
package main
import (
"os"
"github.com/yanosea/mindnum/v2/app/presentation/cli/mindnum/command"
"github.com/yanosea/mindnum/v2/pkg/proxy"
"github.com/yanosea/mindnum/v2/pkg/utility"
)
var (
// version is the version of mindnum cli.
version = ""
// cobra is a proxy of spf13/cobra.
cobra = proxy.NewCobra()
// versionUtil is a provider of the version of the application.
versionUtil = utility.NewVersionUtil(proxy.NewDebug())
// exit is a proxy of os.Exit.
exit = os.Exit
)
// main is the entry point of mindnum cli.
func main() {
cli := command.NewCli(
cobra,
versionUtil.GetVersion(version),
)
exit(cli.Run())
}
package presenter
import (
"fmt"
"io"
)
// PrintFunc is a function type that defines the signature for printing output.
type PrintFunc func(writer io.Writer, output string) error
// Print is a function that writes the output to the writer.
var Print PrintFunc = func(writer io.Writer, output string) error {
if output != "" && output != "\n" {
_, err := fmt.Fprintf(writer, "%s\n", output)
return err
} else {
_, err := fmt.Fprintln(writer)
return err
}
}
package utility
import (
"os"
"github.com/yanosea/mindnum/v2/pkg/proxy"
)
// Capturer is an interface that captures the output of a function.
type Capturer interface {
CaptureOutput(fnc func()) (string, string, error)
}
// capturer is a struct that implements the Captures interface.
type capturer struct {
// os is an interface for operating system functions.
os proxy.Os
// stdBuffer is a buffer for standard output.
stdBuffer proxy.Buffer
// errBuffer is a buffer for error output.
errBuffer proxy.Buffer
}
// NewCapturer returns a new instance of the capturer struct.
func NewCapturer(
os proxy.Os,
stdBuffer proxy.Buffer,
errBuffer proxy.Buffer,
) *capturer {
return &capturer{
os: os,
stdBuffer: stdBuffer,
errBuffer: errBuffer,
}
}
// CaptureOutput captures the output of a function.
func (c *capturer) CaptureOutput(fnc func()) (string, string, error) {
origStdout := os.Stdout
origStderr := os.Stderr
defer func() {
os.Stdout = origStdout
os.Stderr = origStderr
}()
rOut, wOut, err := c.os.Pipe()
if err != nil {
return "", "", err
}
rErr, wErr, err := c.os.Pipe()
if err != nil {
return "", "", err
}
os.Stdout = wOut.(interface{ AsOsFile() *os.File }).AsOsFile()
os.Stderr = wErr.(interface{ AsOsFile() *os.File }).AsOsFile()
fnc()
if err := wOut.Close(); err != nil {
return "", "", err
}
if err := wErr.Close(); err != nil {
return "", "", err
}
if _, err := c.stdBuffer.ReadFrom(rOut); err != nil {
return "", "", err
}
if _, err := c.errBuffer.ReadFrom(rErr); err != nil {
return "", "", err
}
stdout := c.stdBuffer.String()
errout := c.errBuffer.String()
c.stdBuffer.Reset()
c.errBuffer.Reset()
return stdout, errout, nil
}
package utility
import (
"github.com/yanosea/mindnum/v2/pkg/proxy"
)
// VersionUtil is an interface that provides the version of the application.
type VersionUtil interface {
GetVersion(version string) string
}
// versionUtil is a struct that implements the VersionUtil interface.
type versionUtil struct {
Debug proxy.Debug
}
// NewVersionUtil returns a new instance of the versionUtil struct.
func NewVersionUtil(
debug proxy.Debug,
) VersionUtil {
return &versionUtil{
Debug: debug,
}
}
// GetVersion returns the version of the application.
func (v *versionUtil) GetVersion(version string) string {
// if version is embedded, return it
if version != "" {
return version
}
if i, ok := v.Debug.ReadBuildInfo(); !ok {
return "unknown"
} else if i.Main.Version != "" {
return i.Main.Version
} else {
return "dev"
}
}