github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/github.com/antonmedv/expr/file/source.go (about) 1 package file 2 3 import ( 4 "encoding/json" 5 "strings" 6 "unicode/utf8" 7 ) 8 9 type Source struct { 10 contents []rune 11 lineOffsets []int32 12 } 13 14 func NewSource(contents string) *Source { 15 s := &Source{ 16 contents: []rune(contents), 17 } 18 s.updateOffsets() 19 return s 20 } 21 22 func (s *Source) MarshalJSON() ([]byte, error) { 23 return json.Marshal(s.contents) 24 } 25 26 func (s *Source) UnmarshalJSON(b []byte) error { 27 contents := make([]rune, 0) 28 err := json.Unmarshal(b, &contents) 29 if err != nil { 30 return err 31 } 32 33 s.contents = contents 34 s.updateOffsets() 35 return nil 36 } 37 38 func (s *Source) Content() string { 39 return string(s.contents) 40 } 41 42 func (s *Source) Snippet(line int) (string, bool) { 43 charStart, found := s.findLineOffset(line) 44 if !found || len(s.contents) == 0 { 45 return "", false 46 } 47 charEnd, found := s.findLineOffset(line + 1) 48 if found { 49 return string(s.contents[charStart : charEnd-1]), true 50 } 51 return string(s.contents[charStart:]), true 52 } 53 54 // updateOffsets compute line offsets up front as they are referred to frequently. 55 func (s *Source) updateOffsets() { 56 lines := strings.Split(string(s.contents), "\n") 57 offsets := make([]int32, len(lines)) 58 var offset int32 59 for i, line := range lines { 60 offset = offset + int32(utf8.RuneCountInString(line)) + 1 61 offsets[int32(i)] = offset 62 } 63 s.lineOffsets = offsets 64 } 65 66 // findLineOffset returns the offset where the (1-indexed) line begins, 67 // or false if line doesn't exist. 68 func (s *Source) findLineOffset(line int) (int32, bool) { 69 if line == 1 { 70 return 0, true 71 } else if line > 1 && line <= len(s.lineOffsets) { 72 offset := s.lineOffsets[line-2] 73 return offset, true 74 } 75 return -1, false 76 }