///|
/// Whether an address survives ordinary positional edits.
pub enum SelectorStability {
Stable
SnapshotRelative
}
///|
/// The optional selector attached to a named path segment.
pub enum SegmentSelection {
Position(Int)
Key(String, String)
}
///|
/// One named segment in a canonical Office selector. Construction stays
/// package-private so parsed selector invariants cannot be bypassed.
pub struct SelectorSegment {
name : String
selection : SegmentSelection?
}
///|
/// One validated XLSX A1 cell coordinate. Columns and rows are 1-based;
/// construction stays package-private so invalid coordinates cannot render.
pub struct CellAddress {
column : Int
row : Int
}
///|
/// The XLSX-only coordinate leaf of an Office selector.
pub enum SelectorCoordinate {
Cell(CellAddress)
Range(CellAddress, CellAddress)
}
///|
/// A parsed, format-explicit `office.selector/1` address. Instances originate
/// from the parser or syntax adapters so their canonical-form invariants hold.
pub struct OfficeSelector {
format : DocumentFormat
segments : ReadOnlyArray[SelectorSegment]
coordinate : SelectorCoordinate?
stability : SelectorStability
}
///|
/// A bounded, structured selector parse or validation failure. `offset` is a
/// zero-based Unicode-scalar position in the original input; `input` is a
/// bounded echo suitable for diagnostics.
pub(all) suberror SelectorError {
SelectorError(
code~ : String,
offset~ : Int,
input~ : String,
message~ : String
)
}
///|
pub impl Show for SelectorError with fn output(self, logger) {
match self {
SelectorError(code~, offset~, input~, message~) =>
logger.write_string(
"selector error \{code} at offset \{offset} in '\{input}': \{message}",
)
}
}
///|
/// Returns a positional selection when this segment uses one.
pub fn SelectorSegment::position(self : SelectorSegment) -> Int? {
match self.selection {
Some(Position(index)) => Some(index)
_ => None
}
}
///|
/// Returns a named selector value when this segment uses `key`.
pub fn SelectorSegment::key_value(
self : SelectorSegment,
key : StringView,
) -> String? {
match self.selection {
Some(Key(actual, value)) if actual == key.to_owned() => Some(value)
_ => None
}
}
///|
/// Returns the canonical name of this path segment.
pub fn SelectorSegment::name(self : SelectorSegment) -> String {
self.name
}
///|
/// Returns this segment's validated selection, if present.
pub fn SelectorSegment::selection(self : SelectorSegment) -> SegmentSelection? {
self.selection
}
///|
/// Returns the document format named by this selector.
pub fn OfficeSelector::document_format(self : OfficeSelector) -> DocumentFormat {
self.format
}
///|
/// Returns the validated path segments in document order.
pub fn OfficeSelector::segments(
self : OfficeSelector,
) -> ReadOnlyArray[SelectorSegment] {
self.segments
}
///|
/// Returns the optional XLSX coordinate leaf.
pub fn OfficeSelector::coordinate(self : OfficeSelector) -> SelectorCoordinate? {
self.coordinate
}
///|
/// Returns whether this selector is stable across ordinary positional edits.
pub fn OfficeSelector::stability(self : OfficeSelector) -> SelectorStability {
self.stability
}
///|
fn selector_column_name(column : Int) -> String {
let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".to_array()
let reversed : Array[Char] = []
let mut value = column
while value > 0 {
let offset = (value - 1) % 26
reversed.push(alphabet[offset])
value = (value - 1) / 26
}
let output = StringBuilder::new()
let mut index = reversed.length()
while index > 0 {
index = index - 1
output.write_char(reversed[index]) |> ignore
}
output.to_string()
}
///|
/// Renders this coordinate in canonical uppercase A1 form.
pub fn CellAddress::render(self : CellAddress) -> String {
selector_column_name(self.column) + self.row.to_string()
}
///|
/// Returns the validated 1-based column index (`A` = 1, `XFD` = 16384).
pub fn CellAddress::column(self : CellAddress) -> Int {
self.column
}
///|
/// Returns the validated 1-based row index (`1` through `1048576`).
pub fn CellAddress::row(self : CellAddress) -> Int {
self.row
}
///|
fn render_segment_selection(selection : SegmentSelection) -> String {
match selection {
Position(index) => "[\{index}]"
Key(key, value) => "[\{key}=\{Json::string(value).stringify()}]"
}
}
///|
/// Renders this selector in its unique canonical form.
pub fn OfficeSelector::render(self : OfficeSelector) -> String {
let output = StringBuilder::new()
output.write_string("/") |> ignore
output.write_string(self.format.name()) |> ignore
for segment in self.segments {
output.write_string("/") |> ignore
output.write_string(segment.name) |> ignore
match segment.selection {
Some(selection) =>
output.write_string(render_segment_selection(selection)) |> ignore
None => ()
}
}
match self.coordinate {
Some(Cell(address)) => {
output.write_string("/cell[") |> ignore
output.write_string(address.render()) |> ignore
output.write_string("]") |> ignore
}
Some(Range(start, finish)) => {
output.write_string("/range[") |> ignore
output.write_string(start.render()) |> ignore
output.write_string(":") |> ignore
output.write_string(finish.render()) |> ignore
output.write_string("]") |> ignore
}
None => ()
}
output.to_string()
}