package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
)

type Myerror struct {
	error
}

func (Myerror) Error() string {
	//TODO implement me
	fmt.Println("俺真的错了！")
	panic("implement me")
}

const (
	Error = iota + 31
	Info
	Warn
	Debug
	Panic
)

func ProcessErrStr(c int, s error) string {
	return fmt.Sprintf("\x1b[0;%dm%v\x1b[0m", c, s)
}

func main() {
	// errors.New方法可以字符串创建一个error
	// 类似于fmt.Errorf方法
	CheckStr := func(s string) error {
		if s == "" {
			return errors.New("the str must be not empty")
		}
		return nil
	}
	//fmt.Println(CheckStr(""))
	//fmt.Println(CheckStr("abc"))

	// 当错误发生后，一般有三种方式来处理错误

	// 第一种，使用fmt.Fprintf方法将错误打印到stderr， 并终止程序
	if err := CheckStr(""); err != nil {
		if _, err := fmt.Fprintf(os.Stderr, "1. %v\n", ProcessErrStr(Panic, err)); err != nil {
			os.Exit(1)
		}
	}

	// 第二种，使用log.Fatalf,这种方式等于是上面的那种方法，都是输出致命的错误后，退出程序
	// 调用log.Fatalf可以更简洁的代码达到与上文相同的效果。log中的所有函数，都默认会在错误信息之前输出时间信息。
	// 等价于{Printf(v...); os.Exit(1)}
	//if err := CheckStr(""); err != nil {
	//	log.Fatalf("2. %v\n", ProcessErrStr(Error, err))
	//}
	// 我们也可以屏蔽掉log的日期输出
	/* see https://www.flysnow.org/2017/05/06/go-in-action-go-log */
	log.SetPrefix(ProcessErrStr(Error, errors.New("ERR: ")))
	log.SetFlags(log.Ldate | log.Ltime | log.LUTC | log.Lshortfile)
	//if err := CheckStr(""); err != nil {
	//	log.Fatalf("2.1. %v\n", ProcessErrStr(Error, err))
	//}

	// 最后我们也可以直接忽略到错误的发生，不做处理, 仅做stdout输出。
	if err := CheckStr(""); err != nil {
		if _, err := fmt.Fprintf(os.Stdout, "3. %v\n", ProcessErrStr(Panic, err)); err != nil {
			log.Fatalln("标准输出失败")
		}
	}

	// error错误有时并不是代表致命错误，或者程序运行错误，而是根据错误类型，做出不同的相应
	// 其中EOF错误就是一种，调用者会重复的读取固定大小的数据直到文件结束。
	// 这会导致调用者必须分别处理由文件结束引起的各种错误。
	readFile := func() error {
		in := bufio.NewReader(os.Stdin)
		for {
			r, _, err := in.ReadRune()
			if err == io.EOF {
				break // finished reading
			}
			if err != nil {
				return fmt.Errorf("read failed:%v", err)
			}
			// ...use r…
			fmt.Println(r)
		}
		return nil
	}
	if err := readFile(); err != nil {
		log.Fatalln("EOF!")
	}
}
