github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/schemadsl/input/inputsource.go (about) 1 package input 2 3 import ( 4 "fmt" 5 ) 6 7 // BytePosition represents the byte position in a piece of code. 8 type BytePosition int 9 10 // Position represents a position in an arbitrary source file. 11 type Position struct { 12 // LineNumber is the 0-indexed line number. 13 LineNumber int 14 15 // ColumnPosition is the 0-indexed column position on the line. 16 ColumnPosition int 17 } 18 19 // Source represents the path of a source file. 20 type Source string 21 22 // RangeForRunePosition returns a source range over this source file. 23 func (is Source) RangeForRunePosition(runePosition int, mapper PositionMapper) SourceRange { 24 return is.RangeForRunePositions(runePosition, runePosition, mapper) 25 } 26 27 // PositionForRunePosition returns a source position over this source file. 28 func (is Source) PositionForRunePosition(runePosition int, mapper PositionMapper) SourcePosition { 29 return runeIndexedPosition{is, mapper, runePosition} 30 } 31 32 // PositionFromLineAndColumn returns a source position at the given line and column in this source file. 33 func (is Source) PositionFromLineAndColumn(lineNumber int, columnPosition int, mapper PositionMapper) SourcePosition { 34 return lcIndexedPosition{is, mapper, Position{lineNumber, columnPosition}} 35 } 36 37 // RangeForRunePositions returns a source range over this source file. 38 func (is Source) RangeForRunePositions(startRune int, endRune int, mapper PositionMapper) SourceRange { 39 return sourceRange{is, runeIndexedPosition{is, mapper, startRune}, runeIndexedPosition{is, mapper, endRune}} 40 } 41 42 // RangeForLineAndColPositions returns a source range over this source file. 43 func (is Source) RangeForLineAndColPositions(start Position, end Position, mapper PositionMapper) SourceRange { 44 return sourceRange{is, lcIndexedPosition{is, mapper, start}, lcIndexedPosition{is, mapper, end}} 45 } 46 47 // PositionMapper defines an interface for converting rune position <-> line+col position 48 // under source files. 49 type PositionMapper interface { 50 // RunePositionToLineAndCol converts the given 0-indexed rune position under the given source file 51 // into a 0-indexed line number and column position. 52 RunePositionToLineAndCol(runePosition int, path Source) (int, int, error) 53 54 // LineAndColToRunePosition converts the given 0-indexed line number and column position under the 55 // given source file into a 0-indexed rune position. 56 LineAndColToRunePosition(lineNumber int, colPosition int, path Source) (int, error) 57 58 // TextForLine returns the text for the specified line number. 59 TextForLine(lineNumber int, path Source) (string, error) 60 } 61 62 // SourceRange represents a range inside a source file. 63 type SourceRange interface { 64 // Source is the input source for this range. 65 Source() Source 66 67 // Start is the starting position of the source range. 68 Start() SourcePosition 69 70 // End is the ending position (inclusive) of the source range. If the same as the Start, 71 // this range represents a single position. 72 End() SourcePosition 73 74 // ContainsPosition returns true if the given range contains the given position. 75 ContainsPosition(position SourcePosition) (bool, error) 76 77 // AtStartPosition returns a SourceRange located only at the starting position of this range. 78 AtStartPosition() SourceRange 79 80 // String returns a (somewhat) human-readable form of the range. 81 String() string 82 } 83 84 // SourcePosition represents a single position in a source file. 85 type SourcePosition interface { 86 // Source is the input source for this position. 87 Source() Source 88 89 // RunePosition returns the 0-indexed rune position in the source file. 90 RunePosition() (int, error) 91 92 // LineAndColumn returns the 0-indexed line number and column position in the source file. 93 LineAndColumn() (int, int, error) 94 95 // LineText returns the text of the line for this position. 96 LineText() (string, error) 97 98 // String returns a (somewhat) human-readable form of the position. 99 String() string 100 } 101 102 // sourceRange implements the SourceRange interface. 103 type sourceRange struct { 104 source Source 105 start SourcePosition 106 end SourcePosition 107 } 108 109 func (sr sourceRange) Source() Source { 110 return sr.source 111 } 112 113 func (sr sourceRange) Start() SourcePosition { 114 return sr.start 115 } 116 117 func (sr sourceRange) End() SourcePosition { 118 return sr.end 119 } 120 121 func (sr sourceRange) AtStartPosition() SourceRange { 122 return sourceRange{sr.source, sr.start, sr.end} 123 } 124 125 func (sr sourceRange) ContainsPosition(position SourcePosition) (bool, error) { 126 if position.Source() != sr.source { 127 return false, nil 128 } 129 130 startRune, err := sr.start.RunePosition() 131 if err != nil { 132 return false, err 133 } 134 135 endRune, err := sr.end.RunePosition() 136 if err != nil { 137 return false, err 138 } 139 140 positionRune, err := position.RunePosition() 141 if err != nil { 142 return false, err 143 } 144 145 return positionRune >= startRune && positionRune <= endRune, nil 146 } 147 148 func (sr sourceRange) String() string { 149 return fmt.Sprintf("%v -> %v", sr.start, sr.end) 150 } 151 152 // runeIndexedPosition implements the SourcePosition interface over a rune position. 153 type runeIndexedPosition struct { 154 source Source 155 mapper PositionMapper 156 runePosition int 157 } 158 159 func (ris runeIndexedPosition) Source() Source { 160 return ris.source 161 } 162 163 func (ris runeIndexedPosition) RunePosition() (int, error) { 164 return ris.runePosition, nil 165 } 166 167 func (ris runeIndexedPosition) LineAndColumn() (int, int, error) { 168 if ris.runePosition == 0 { 169 return 0, 0, nil 170 } 171 if ris.mapper == nil { 172 return -1, -1, fmt.Errorf("nil mapper") 173 } 174 return ris.mapper.RunePositionToLineAndCol(ris.runePosition, ris.source) 175 } 176 177 func (ris runeIndexedPosition) String() string { 178 return fmt.Sprintf("%s@%v (rune)", ris.source, ris.runePosition) 179 } 180 181 func (ris runeIndexedPosition) LineText() (string, error) { 182 lineNumber, _, err := ris.LineAndColumn() 183 if err != nil { 184 return "", err 185 } 186 187 return ris.mapper.TextForLine(lineNumber, ris.source) 188 } 189 190 // lcIndexedPosition implements the SourcePosition interface over a line and colu,n position. 191 type lcIndexedPosition struct { 192 source Source 193 mapper PositionMapper 194 lcPosition Position 195 } 196 197 func (lcip lcIndexedPosition) Source() Source { 198 return lcip.source 199 } 200 201 func (lcip lcIndexedPosition) String() string { 202 return fmt.Sprintf("%s@%v:%v (line/col)", lcip.source, lcip.lcPosition.LineNumber, lcip.lcPosition.ColumnPosition) 203 } 204 205 func (lcip lcIndexedPosition) RunePosition() (int, error) { 206 if lcip.lcPosition.LineNumber == 0 && lcip.lcPosition.ColumnPosition == 0 { 207 return 0, nil 208 } 209 if lcip.mapper == nil { 210 return -1, fmt.Errorf("nil mapper") 211 } 212 return lcip.mapper.LineAndColToRunePosition(lcip.lcPosition.LineNumber, lcip.lcPosition.ColumnPosition, lcip.source) 213 } 214 215 func (lcip lcIndexedPosition) LineAndColumn() (int, int, error) { 216 return lcip.lcPosition.LineNumber, lcip.lcPosition.ColumnPosition, nil 217 } 218 219 func (lcip lcIndexedPosition) LineText() (string, error) { 220 if lcip.mapper == nil { 221 return "", fmt.Errorf("nil mapper") 222 } 223 return lcip.mapper.TextForLine(lcip.lcPosition.LineNumber, lcip.source) 224 }