示例#1
1
文件: types.go 项目: caixw/apidoc
func (cfg *config) init() *app.OptionsError {
	if !version.SemVerValid(cfg.Version) {
		return &app.OptionsError{Field: "version", Message: locale.Sprintf(locale.ErrInvalidFormat)}
	}

	if len(cfg.Inputs) == 0 {
		return &app.OptionsError{Field: "inputs", Message: locale.Sprintf(locale.ErrRequired)}
	}

	if cfg.Output == nil {
		return &app.OptionsError{Field: "output", Message: locale.Sprintf(locale.ErrRequired)}
	}

	for i, opt := range cfg.Inputs {
		if err := opt.Init(); err != nil {
			index := strconv.Itoa(i)
			err.Field = "inputs[" + index + "]." + err.Field
			return err
		}
		opt.SyntaxLog = erro // 语法错误输出到 erro 中
	}

	if err := cfg.Output.Init(); err != nil {
		err.Field = "outputs." + err.Field
		return err
	}

	return nil
}
示例#2
1
文件: main.go 项目: caixw/apidoc
// 真正的程序入口,main 主要是作参数的处理。
func run() {
	start := time.Now()

	path, err := getConfigFile()
	if err != nil {
		erro.Println(err)
		return
	}

	cfg, err := loadConfig(path)
	if err != nil {
		erro.Println(err)
		return
	}

	// 比较版本号兼容问题
	compatible, err := version.SemVerCompatible(app.Version, cfg.Version)
	if err != nil {
		erro.Println(err)
		return
	}
	if !compatible {
		erro.Println(locale.Sprintf(locale.VersionInCompatible))
		return
	}

	// 分析文档内容
	docs := doc.New()
	wg := &sync.WaitGroup{}
	for _, opt := range cfg.Inputs {
		wg.Add(1)
		go func(o *input.Options) {
			if err := input.Parse(docs, o); err != nil {
				erro.Println(err)
			}
			wg.Done()
		}(opt)
	}
	wg.Wait()

	if len(docs.Title) == 0 {
		docs.Title = app.DefaultTitle
	}

	// 输出内容
	cfg.Output.Elapsed = time.Now().Sub(start)
	if err := output.Render(docs, cfg.Output); err != nil {
		erro.Println(err)
		return
	}

	info.Println(locale.Sprintf(locale.Complete, cfg.Output.Dir, time.Now().Sub(start)))
}
示例#3
0
文件: block.go 项目: caixw/apidoc
func (b *block) EndFunc(l *lexer) ([]rune, bool) {
	switch b.Type {
	case blockTypeString:
		return b.endString(l)
	case blockTypeMComment:
		return b.endMComments(l)
	case blockTypeSComment:
		return b.endSComments(l)
	default:
		panic(locale.Sprintf(locale.ErrInvalidBlockType, b.Type))
	}
}
示例#4
0
文件: output.go 项目: caixw/apidoc
// Render 渲染 docs 的内容,具体的渲染参数由 o 指定。
func Render(docs *doc.Doc, o *Options) error {
	switch o.Type {
	case "html":
		return renderHTML(docs, o)
	case "html+":
		return renderHTMLPlus(docs, o)
	case "json":
		return renderJSON(docs, o)
	default:
		return &app.OptionsError{Field: "Type", Message: locale.Sprintf(locale.ErrInvalidOutputType)}
	}
}
示例#5
0
文件: lang.go 项目: caixw/apidoc
// DetectDirLang 检测指定目录下的语言类型。
//
// 检测依据为根据扩展名来做统计,数量最大且被支持的获胜。
// 不会分析子目录。
func DetectDirLang(dir string) (string, error) {
	fs, err := ioutil.ReadDir(dir)
	if err != nil {
		return "", err
	}

	// langsMap 记录每个支持的语言对应的文件数量
	langsMap := make(map[string]int, len(fs))
	for _, f := range fs { // 遍历所有的文件
		if f.IsDir() {
			continue
		}

		ext := strings.ToLower(filepath.Ext(f.Name()))
		lang := getLangByExt(ext)
		if len(lang) > 0 {
			langsMap[lang]++
		}
	}

	if len(langsMap) == 0 {
		return "", errors.New(locale.Sprintf(locale.ErrNotFoundSupportedLang))
	}

	lang := ""
	cnt := 0
	for k, v := range langsMap {
		if v >= cnt {
			lang = k
			cnt = v
		}
	}

	if len(lang) > 0 {
		return lang, nil
	}
	return "", errors.New(locale.Sprintf(locale.ErrNotFoundSupportedLang))
}
示例#6
0
文件: input.go 项目: caixw/apidoc
// Init 检测 Options 变量是否符合要求
func (opt *Options) Init() *app.OptionsError {
	if len(opt.Dir) == 0 {
		return &app.OptionsError{Field: "dir", Message: locale.Sprintf(locale.ErrRequired)}
	}

	if !utils.FileExists(opt.Dir) {
		return &app.OptionsError{Field: "dir", Message: locale.Sprintf(locale.ErrDirNotExists)}
	}

	if len(opt.Lang) == 0 {
		return &app.OptionsError{Field: "lang", Message: locale.Sprintf(locale.ErrRequired)}
	}

	if !langIsSupported(opt.Lang) {
		return &app.OptionsError{Field: "lang", Message: locale.Sprintf(locale.ErrUnsupportedInputLang, opt.Lang)}
	}

	if len(opt.Exts) > 0 {
		exts := make([]string, 0, len(opt.Exts))
		for _, ext := range opt.Exts {
			if len(ext) == 0 {
				continue
			}

			if ext[0] != '.' {
				ext = "." + ext
			}
			exts = append(exts, ext)
		}
		opt.Exts = exts
	} else {
		opt.Exts = langExts[opt.Lang]
	}

	return nil
}
示例#7
0
文件: input.go 项目: caixw/apidoc
// 分析 path 指向的文件,并将内容写入到 docs 中。
func parseFile(docs *doc.Doc, path string, blocks []blocker, synerrLog *log.Logger) {
	data, err := ioutil.ReadFile(path)
	if err != nil && synerrLog != nil {
		synerrLog.Println(&app.SyntaxError{Message: err.Error(), File: path})
		return
	}

	l := &lexer{data: data}
	var block blocker

	wg := sync.WaitGroup{}
	defer wg.Wait()

	for {
		if l.atEOF() {
			return
		}

		if block == nil {
			block = l.block(blocks)
			if block == nil { // 没有匹配的 block 了
				return
			}
		}

		ln := l.lineNumber() + 1 // 记录当前的行号,顺便调整为行号起始行号为 1
		rs, ok := block.EndFunc(l)
		if !ok && synerrLog != nil {
			synerrLog.Println(&app.SyntaxError{Line: ln, File: path, Message: locale.Sprintf(locale.ErrNotFoundEndFlag)})
			return
		}

		block = nil
		if len(rs) < miniSize {
			continue
		}

		wg.Add(1)
		go func(rs []rune, ln int) {
			if err := docs.Scan(rs); err != nil && synerrLog != nil {
				err.Line += ln
				err.File = path
				synerrLog.Println(err)
			}
			wg.Done()
		}(rs, ln)
	} // end for
}
示例#8
0
文件: output.go 项目: caixw/apidoc
// Init 对 Options 作一些初始化操作。
func (o *Options) Init() *app.OptionsError {
	if len(o.Dir) == 0 {
		return &app.OptionsError{Field: "dir", Message: locale.Sprintf(locale.ErrRequired)}
	}

	if len(o.Type) == 0 {
		return &app.OptionsError{Field: "type", Message: locale.Sprintf(locale.ErrRequired)}
	}

	if !utils.FileExists(o.Dir) {
		if err := os.MkdirAll(o.Dir, os.ModePerm); err != nil {
			msg := locale.Sprintf(locale.ErrMkdirError, err)
			return &app.OptionsError{Field: "dir", Message: msg}
		}
	}

	if !isSuppertedType(o.Type) {
		return &app.OptionsError{Field: "type", Message: locale.Sprintf(locale.ErrInvalidFormat)}
	}

	// 只有 html 和 html+ 才需要判断模板文件是否存在
	if o.Type == "html" || o.Type == "html+" {
		if len(o.Template) > 0 && !utils.FileExists(o.Template) {
			msg := locale.Sprintf(locale.ErrTemplateNotExists)
			return &app.OptionsError{Field: "template", Message: msg}
		}
	}

	// 调试模式,必须得有模板和端口
	if o.Type == "html+" {
		if len(o.Template) == 0 {
			return &app.OptionsError{Field: "template", Message: locale.Sprintf(locale.ErrRequired)}
		}

		if len(o.Port) == 0 {
			return &app.OptionsError{Field: "port", Message: locale.Sprintf(locale.ErrRequired)}
		}

		if o.Port[0] != ':' {
			o.Port = ":" + o.Port
		}
	}

	return nil
}
示例#9
0
文件: input.go 项目: caixw/apidoc
// Parse 分析源代码,获取相应的文档内容。
func Parse(docs *doc.Doc, o *Options) error {
	blocks, found := langs[o.Lang]
	if !found {
		return errors.New(locale.Sprintf(locale.ErrUnsupportedInputLang, o.Lang))
	}

	paths, err := recursivePath(o)
	if err != nil {
		return err
	}

	wg := sync.WaitGroup{}
	defer wg.Wait()
	for _, path := range paths {
		wg.Add(1)
		go func(path string) {
			parseFile(docs, path, blocks, o.SyntaxLog)
			wg.Done()
		}(path)
	}

	return nil
}
示例#10
0
文件: lexer.go 项目: caixw/apidoc
// 构建一个语法错误的信息。
func (l *lexer) syntaxError(format string, v ...interface{}) *app.SyntaxError {
	return &app.SyntaxError{
		Line:    l.lineNumber(),
		Message: locale.Sprintf(format, v...),
	}
}
示例#11
0
文件: errors.go 项目: caixw/apidoc
func (err *OptionsError) Error() string {
	return locale.Sprintf(locale.OptionsError, ConfigFilename, err.Field, err.Message)
}
示例#12
0
文件: errors.go 项目: caixw/apidoc
func (err *SyntaxError) Error() string {
	return locale.Sprintf(locale.SyntaxError, err.File, err.Line, err.Message)
}
示例#13
0
文件: main.go 项目: caixw/apidoc
func main() {
	tag, err := locale.Init()
	if err != nil {
		warn.Println(err)
		info.Println("无法获取系统语言,使用默认的本化语言:", app.DefaultLocale)
		tag, err = language.Parse(app.DefaultLocale)
		if err != nil {
			erro.Println(err)
			return
		}
	}
	locale.SetLocale(tag)

	h := flag.Bool("h", false, locale.Sprintf(locale.FlagHUsage))
	v := flag.Bool("v", false, locale.Sprintf(locale.FlagVUsage))
	l := flag.Bool("l", false, locale.Sprintf(locale.FlagLUsage))
	g := flag.Bool("g", false, locale.Sprintf(locale.FlagGUsage))
	pprofType := flag.String("pprof", "", locale.Sprintf(locale.FlagPprofUsage))
	flag.Usage = usage
	flag.Parse()

	switch {
	case *h:
		flag.Usage()
		return
	case *v:
		locale.Printf(locale.FlagVersionBuildWith, app.Name, app.Version, runtime.Version())
		return
	case *l:
		locale.Printf(locale.FlagSupportedLangs, input.Langs())
		return
	case *g:
		path, err := getConfigFile()
		if err != nil {
			erro.Println(err)
			return
		}
		if err = genConfigFile(path); err != nil {
			erro.Println(err)
			return
		}
		info.Println(locale.Sprintf(locale.FlagConfigWritedSuccess, path))
		return
	}

	// 指定了 pprof 参数
	if len(*pprofType) > 0 {
		profile := filepath.Join("./", app.Profile)
		f, err := os.Create(profile)
		if err != nil { // 不能创建文件,则忽略 pprof 相关操作
			warn.Println(err)
			goto RUN
		}
		defer func() {
			if err = f.Close(); err != nil {
				erro.Println(err)
				return
			}
			info.Println(locale.Sprintf(locale.FlagPprofWritedSuccess, profile))
		}()

		switch strings.ToLower(*pprofType) {
		case "mem":
			defer func() {
				if err = pprof.Lookup("heap").WriteTo(f, 1); err != nil {
					warn.Println(err)
				}
			}()
		case "cpu":
			if err := pprof.StartCPUProfile(f); err != nil {
				warn.Println(err)
			}
			defer pprof.StopCPUProfile()
		default:
			erro.Println(locale.Sprintf(locale.FlagInvalidPprrof))
			return
		}
	}

RUN:
	run()
}