parser.go 5.07 KB
package parser

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"strings"
)

type jsonObject map[string]interface{}
type jsonArray []interface{}

type Request struct {
	Log   []string `json:"log"`
	Sheet Sheet    `json:"sheet"`
}

type Sheet struct {
	LogID     string `json:"log_id"`
	Date      string `json:"date"`
	Target    string `json:"target"`
	Memory    string `json:"memory"`
	StartLine string `json:"start_line"`

	Hardware []SheetEntry `json:"hardware"`
	OS       []SheetEntry `json:"os"`
	Device   []SheetEntry `json:"device"`
}

type SheetEntry struct {
	Title        string     `json:"title"`
	Check        [][]string `json:"check"`
	ErrorMessage []string   `json:"error_message"`
}

type Parser struct {
	InChan  chan string
	OutChan chan jsonObject
}

func New() *Parser {
	parser := &Parser{}
	return parser
}

func (p *Parser) Parse(request *Request) string {
	p.start()
	for _, line := range request.Log {
		p.InChan <- line
	}
	p.stop()

	return p.makeResult(request.Sheet)
}

func (p *Parser) start() {
	p.InChan = make(chan string, 100)
	p.OutChan = make(chan jsonObject, 1)

	// Go parse functions
	inputChanList := make([]chan string, len(parseFuncList))
	outputChanList := make([]chan jsonObject, len(parseFuncList))
	for i, parseFunc := range parseFuncList {
		f := parseFunc

		inputChan := make(chan string, 10)
		outputChan := make(chan jsonObject, 1)

		inputChanList[i] = inputChan
		outputChanList[i] = outputChan

		go func() {
			defer func() {
				if r := recover(); r != nil {
					//p.Fatal("Internal Error", r, string(debug.Stack()))
				}
				for range inputChan {
				}
				close(outputChan)
			}()
			f(inputChan, outputChan)
		}()
	}

	// Broadcast input
	go func() {
		for input := range p.InChan {
			for _, inputChan := range inputChanList {
				inputChan <- input
			}
		}

		for _, inputChan := range inputChanList {
			close(inputChan)
		}
	}()

	// Merge output
	go func() {
		output := make(jsonObject)

		for _, outputChan := range outputChanList {
			for parsed := range outputChan {
				output[parsed["type"].(string)] = parsed
			}
		}

		p.OutChan <- output
		close(p.OutChan)
	}()
}

func (p *Parser) stop() {
	close(p.InChan)
}

func (p *Parser) makeReport(result jsonObject, checklist []SheetEntry) ([]jsonObject, bool) {
	var report []jsonObject

	totalPass := true

	for _, entry := range checklist {
		reportEntry := make(jsonObject)
		reportEntry["title"] = entry.Title

		pass := true
		for _, c := range entry.Check {
			o, ok := result[c[0]].(jsonObject)
			if !ok {
				reportEntry["result"] = "FAIL"
				reportEntry["message"] = entry.ErrorMessage[0]
				pass = false
				break
			}
			switch o[c[1]].(type) {
			case string:
				if len(c[2]) > 0 {
					v, ok := o[c[1]].(string)
					if !ok {
						reportEntry["result"] = "FAIL"
						reportEntry["message"] = entry.ErrorMessage[0]
						pass = false
						break
					}
					if strings.Compare(v, c[2]) != 0 {
						reportEntry["result"] = "FAIL"
						reportEntry["message"] = entry.ErrorMessage[0]
						pass = false
						break
					}
				}
			case []string:
				if len(c[2]) > 0 {
					l, ok := o[c[1]].([]string)
					if !ok {
						reportEntry["result"] = "FAIL"
						reportEntry["message"] = entry.ErrorMessage[0]
						pass = false
						break
					}

					found := false
					for _, v := range l {
						if strings.Compare(v, c[2]) == 0 {
							found = true
						}
					}
					if !found {
						reportEntry["result"] = "FAIL"
						reportEntry["message"] = entry.ErrorMessage[0]
						pass = false
						break
					}
				}
			case []jsonObject:
				if len(c[2]) > 0 {
					list, ok := o[c[1]].([]jsonObject)
					if !ok {
						reportEntry["result"] = "FAIL"
						reportEntry["message"] = entry.ErrorMessage[0]
						pass = false
						break
					}

					found := false
					for _, v := range list {
						label := v["label"].(string)
						if strings.Compare(label, c[2]) == 0 {
							found = true
						}
					}
					if !found {
						reportEntry["result"] = "FAIL"
						reportEntry["message"] = entry.ErrorMessage[0]
						pass = false
						break
					}
				}
			default:
				fmt.Printf("%v fail (type: %T)\n", c[1], o[c[1]])
				reportEntry["result"] = "FAIL"
				pass = false
			}
		}
		if pass {
			reportEntry["result"] = "PASS"
		} else {
			totalPass = false
		}
		report = append(report, reportEntry)
	}

	return report, totalPass
}

func (p *Parser) makeResult(sheet Sheet) string {
	output := make(jsonObject)

	result := <-p.OutChan
	report := make(jsonObject)

	pass := true
	totalPass := true

	result["memory_size"] = sheet.Memory

	report["log_id"] = sheet.LogID
	report["date"] = sheet.Date
	report["target"] = sheet.Target

	report["hardware"], pass = p.makeReport(result, sheet.Hardware)
	totalPass = totalPass && pass
	report["os"], pass = p.makeReport(result, sheet.OS)
	totalPass = totalPass && pass
	report["device"], pass = p.makeReport(result, sheet.Device)
	totalPass = totalPass && pass

	if totalPass {
		report["pass"] = "PASS"
	}

	output["result"] = result
	output["report"] = report

	b, err := json.MarshalIndent(output, "", "  ")
	if err == nil {
		ioutil.WriteFile("anal-kernel-response.json", b, 0644)
	}
	return string(b)
}