1 Star 0 Fork 0

天雨流芳 / go-micro-framework

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
app.go 9.18 KB
一键复制 编辑 原始数据 按行查看 历史
天雨流芳 提交于 2024-03-19 19:58 . 通用的应用启动逻辑封装
package appcore
import (
"fmt"
cliflag "gitee.com/tylf2018/go-micro-framework/pkg/common/cli/flag"
"gitee.com/tylf2018/go-micro-framework/pkg/common/cli/globalflag"
"gitee.com/tylf2018/go-micro-framework/pkg/common/term"
"gitee.com/tylf2018/go-micro-framework/pkg/common/version"
"gitee.com/tylf2018/go-micro-framework/pkg/common/version/verflag"
"gitee.com/tylf2018/go-micro-framework/pkg/config"
"gitee.com/tylf2018/go-micro-framework/pkg/errors"
"gitee.com/tylf2018/go-micro-framework/pkg/logger"
"gitee.com/tylf2018/go-micro-framework/pkg/options"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"os"
"runtime"
"strings"
)
var (
progressMessage = color.GreenString("==>")
)
// CoreApp 是一个命令行应用程序的主要结构。
// 建议使用 app.NewCoreApp() 函数创建一个 app
type CoreApp struct {
basename string
name string
description string
// 应用程序的配置
appOptions CliConfigOptions
// 主程序的配置,主要用于解析应用程序的配置的方式
mainConfigOptions *config.ConfigOptions
runFunc RunFunc
// 表示当命令出现错误时,不会打印使用情况
silence bool
noVersion bool
noConfig bool
commands []*Command
args cobra.PositionalArgs
cmd *cobra.Command
}
// NewCoreApp 根据给定的应用程序名称、项目文件名称以及其他选项创建一个新的应用程序实例
func NewCoreApp(name string, basename string, opts ...Option) *CoreApp {
a := &CoreApp{
name: name, // 启动 app 名字
basename: basename, // 启动 项目 名字
mainConfigOptions: config.NewConfigOptions(),
}
for _, opt := range opts { // 执行附加参数
opt(a)
}
a.buildCommand()
return a
}
// Run 用于启动应用程序
func (a *CoreApp) Run() {
if err := a.cmd.Execute(); err != nil {
fmt.Printf("%v %v\n", color.RedString("Error:"), err)
os.Exit(1)
}
}
// CliConfigOptions abstracts configuration options for reading parameters from the command line.
// 配置文件实现的的添加和验证flag命令行的接口
type CliConfigOptions interface {
Flags() (fss cliflag.NamedFlagSets) // 生成一个被 cliflag.NamedFlagSets 封装后的日志对象
Validate() []error // 添加你想要的验证函数
}
// RunFunc 定义应用程序的启动回调函数
type RunFunc func(basename string) error
// Option 定义了初始化应用程序结构的可选参数
type Option func(*CoreApp)
// WithOptions 用于开启应用程序从命令行读取参数或从配置文件中读取参数的功能
func WithOptions(opt CliConfigOptions) Option {
return func(a *CoreApp) {
a.appOptions = opt
}
}
// WithRunFunc 用于设置应用程序启动回调函数的选项
func WithRunFunc(run RunFunc) Option {
return func(a *CoreApp) {
a.runFunc = run
}
}
// WithDescription 用于设置应用程序的描述
func WithDescription(desc string) Option {
return func(a *CoreApp) {
a.description = desc
}
}
// WithSilence 用于将应用程序设置为静默模式,在该模式下,程序启动信息、配置信息和版本信息不会在控制台中打印出来
func WithSilence() Option {
return func(a *CoreApp) {
a.silence = true
}
}
// WithNoVersion 设置应用程序不提供版本标志
func WithNoVersion() Option {
return func(a *CoreApp) {
a.noVersion = true
}
}
// WithNoConfig 设置应用程序 不使用 config 文件
func WithNoConfig() Option {
return func(a *CoreApp) {
a.noConfig = true
}
}
// WithValidArgs set the validation function to valid non-flag arguments.
func WithValidArgs(args cobra.PositionalArgs) Option {
return func(a *CoreApp) {
a.args = args
}
}
// WithDefaultValidArgs set default validation function to valid non-flag arguments.
func WithDefaultValidArgs() Option {
return func(a *CoreApp) {
a.args = func(cmd *cobra.Command, args []string) error {
for _, arg := range args {
if len(arg) > 0 {
return fmt.Errorf("%q does not take any arguments, got %q", cmd.CommandPath(), args)
}
}
return nil
}
}
}
func (a *CoreApp) buildCommand() {
cmd := cobra.Command{
Use: FormatBaseName(a.basename),
Short: a.name,
Long: a.description,
// stop printing usage when the command errors
SilenceUsage: true, // 表示当命令出现错误时,不会打印使用情况
SilenceErrors: true, // 表示当命令出现错误时,不会输出任何内容
Args: a.args,
}
// cmd.SetUsageTemplate(usageTemplate)
cmd.SetOut(os.Stdout) // 输入到标准输出流(cobra 默认 不填也没事 )
cmd.SetErr(os.Stderr) // 输出到 标准错误输出流(cobra 默认 不填也没事)
cmd.Flags().SortFlags = true // 参数配置排序 按照字母顺序显示在帮助文档中
cliflag.InitFlags(cmd.Flags()) // 规范化标志的名称和值 并且让 Cobra 库支持 Go 标准库 flag 包的所有功能
// 设置 cobra 子命令
if len(a.commands) > 0 {
for _, command := range a.commands {
cmd.AddCommand(command.cobraCommand())
}
// 设置帮助命令
cmd.SetHelpCommand(helpCommand(a.name))
}
// 设置 corbra.RunE 解析成功后 运行此方法
if a.runFunc != nil {
cmd.RunE = a.runCommand
}
fs := cmd.Flags()
// 首先添加配置文件的提示信息
var namedFlagSets cliflag.NamedFlagSets
if a.appOptions != nil {
namedFlagSets = a.appOptions.Flags()
}
if !a.noConfig {
a.mainConfigOptions.AddFlags(namedFlagSets.FlagSet("config"))
}
if len(namedFlagSets.FlagSets) > 0 {
for _, f := range namedFlagSets.FlagSets { // 遍历 map[string]*pflag.FlagSet
fs.AddFlagSet(f) // 集成到 cobra 中 AddFlagSet将一个 FlagSet 添加到另一个 FlagSet
}
usageFmt := "Usage:\n %s\n"
cols, _, _ := term.TerminalSize(cmd.OutOrStdout())
cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
fmt.Fprintf(cmd.OutOrStdout(), "%s\n\n"+usageFmt, cmd.Long, cmd.UseLine()) // 输出到指定输出流中
cliflag.PrintSections(cmd.OutOrStdout(), namedFlagSets, cols) // cols:一个整数,表示终端的列数,用于控制输出的宽度。
})
cmd.SetUsageFunc(func(cmd *cobra.Command) error {
fmt.Fprintf(cmd.OutOrStderr(), usageFmt, cmd.UseLine())
cliflag.PrintSections(cmd.OutOrStderr(), namedFlagSets, cols) // cols:一个整数,表示终端的列数,用于控制输出的宽度。
return nil
})
}
if !a.noVersion {
// 添加版本号标志 到 global flagSet 下
verflag.AddFlags(namedFlagSets.FlagSet("global"))
}
if !a.noConfig {
// 首先从env中获取参数
config.InitConfigFromEnv(a.mainConfigOptions)
}
globalflag.AddGlobalFlags(namedFlagSets.FlagSet("global"), cmd.Name())
a.cmd = &cmd
}
// cobra 最后执行的函数
func (a *CoreApp) runCommand(cmd *cobra.Command, args []string) error {
// 打印工作的目录
printWorkingDir()
// 打印所有的 Flags
cliflag.PrintFlags(cmd.Flags())
if !a.noVersion {
// display application version information
verflag.PrintAndExitIfRequested()
}
// 如果不是不需要读取配置文件的设置,首先读取配置文件
if !a.noConfig {
err := viper.BindPFlags(cmd.Flags())
if err != nil {
return err
}
// 适配一下cmd的主配置
if err := viper.Unmarshal(a.mainConfigOptions); err != nil {
return err
}
// 先从命令行读取
if err := viper.Unmarshal(a.appOptions); err != nil {
return err
}
// 从配置文件中获取
config.AnalyzeConfig(a.mainConfigOptions, a.appOptions)
}
if !a.silence {
logger.InfoF("%v Starting %s ...", progressMessage, a.name)
if !a.noVersion {
logger.InfoF("%v Version: `%s`", progressMessage, version.Get().ToJSON())
}
if !a.noConfig {
logger.InfoF("%v Config file used: `%s`", progressMessage, viper.ConfigFileUsed())
}
}
if a.appOptions != nil {
if err := a.applyOptionRules(); err != nil {
return err
}
}
// run application
if a.runFunc != nil {
return a.runFunc(a.basename)
}
return nil
}
func (a *CoreApp) applyOptionRules() error {
// 这里可以添加 自定义规则 只需要 添加 Complete方法
if completeableOptions, ok := a.appOptions.(options.CompletableOptions); ok {
if err := completeableOptions.Complete(); err != nil {
return err
}
}
// 验证 字段是否合规
if errs := a.appOptions.Validate(); len(errs) != 0 {
return errors.NewAggregate(errs)
}
// 这里可以天机 String 方法 就会打印输出以下的内容
if printableOptions, ok := a.appOptions.(options.PrintableOptions); ok && !a.silence {
logger.InfoF("%v Config: `%s`", progressMessage, printableOptions.String())
}
return nil
}
func printWorkingDir() {
wd, _ := os.Getwd()
logger.InfoF("%v WorkingDir: %s", progressMessage, wd)
}
// Command 方法返回应用程序中的 cobra.Command 实例
func (a *CoreApp) Command() *cobra.Command {
return a.cmd
}
// AddCommand 向应用程序添加多个子命令
func (a *CoreApp) AddCommand(cmds ...*Command) {
a.commands = append(a.commands, cmds...)
}
// FormatBaseName 根据给定的名称,根据不同操作系统格式化为可执行文件名
func FormatBaseName(basename string) string {
// Make case-insensitive and strip executable suffix if present
if runtime.GOOS == "windows" {
basename = strings.ToLower(basename)
basename = strings.TrimSuffix(basename, ".exe")
}
return basename
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/tylf2018/go-micro-framework.git
git@gitee.com:tylf2018/go-micro-framework.git
tylf2018
go-micro-framework
go-micro-framework
4cc90ded505a

搜索帮助

344bd9b3 5694891 D2dac590 5694891