| 
									
										
										
										
											2024-01-26 19:40:38 -06:00
										 |  |  | package models | 
					
						
							| 
									
										
										
										
											2024-02-03 18:52:12 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bufio" | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"regexp" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var reg = regexp.MustCompile("(?sm)^@begin .+?(^| )@end") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Log struct { | 
					
						
							|  |  |  | 	Name    string | 
					
						
							|  |  |  | 	Entries []Entry | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *Log) UnmarshalText(in []byte) error { | 
					
						
							|  |  |  | 	ch := l.getLogUnarshalChan(in) | 
					
						
							|  |  |  | 	for entry := range ch { | 
					
						
							|  |  |  | 		l.Entries = append(l.Entries, entry) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func scanLog(data []byte, atEOF bool) (advance int, token []byte, err error) { | 
					
						
							|  |  |  | 	if atEOF && len(data) == 0 { | 
					
						
							|  |  |  | 		// done | 
					
						
							|  |  |  | 		return 0, nil, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	m := reg.FindIndex(data) | 
					
						
							|  |  |  | 	if len(m) == 0 && atEOF { | 
					
						
							|  |  |  | 		// all trash | 
					
						
							|  |  |  | 		return len(data), nil, nil | 
					
						
							|  |  |  | 	} else if len(m) == 0 && !atEOF { | 
					
						
							|  |  |  | 		// get more | 
					
						
							|  |  |  | 		return 0, nil, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return m[1], data[m[0]:m[1]], nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *Log) getLogUnarshalChan(in []byte) chan Entry { | 
					
						
							|  |  |  | 	size := len(in) / 10 // rough estimation | 
					
						
							|  |  |  | 	ch := make(chan Entry, size) | 
					
						
							|  |  |  | 	var wg sync.WaitGroup | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	read := bytes.NewReader(in) | 
					
						
							|  |  |  | 	scan := bufio.NewScanner(read) | 
					
						
							|  |  |  | 	scan.Split(scanLog) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for scan.Scan() { | 
					
						
							|  |  |  | 		wg.Add(1) | 
					
						
							|  |  |  | 		go func(field []byte) { | 
					
						
							|  |  |  | 			defer wg.Done() | 
					
						
							|  |  |  | 			f := new(Entry) | 
					
						
							|  |  |  | 			err := f.UnmarshalText(field) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			ch <- *f | 
					
						
							|  |  |  | 		}(scan.Bytes()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		wg.Wait() | 
					
						
							|  |  |  | 		close(ch) | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	return ch | 
					
						
							|  |  |  | } |