///|
fn newline_for(doc : Document) -> String {
for line in scan_lines(doc.source) {
if line.end > line.content_end {
return text(doc.source, line.content_end, line.end)
}
}
"\n"
}
///|
fn valid_names(section : String, key : String, dialect : Dialect) -> Bool {
if key == "" ||
key.contains("\r") ||
key.contains("\n") ||
section.contains("\r") ||
section.contains("\n") {
return false
}
let probe_source = if section == "" {
key + "=x"
} else {
"[" + section + "]\n" + key + "=x"
}
let probe = parse(probe_source, dialect~)
let expected = if section == "" { 1 } else { 2 }
!probe.has_errors() &&
probe.nodes.length() == expected &&
probe.nodes[expected - 1].key == key &&
probe.nodes[expected - 1].section == section &&
probe.get(section, key) == Some("x")
}
///|
/// Append a new key to the last matching section block; create a section if absent.
/// Existing text remains an unchanged prefix/suffix; new lines use first observed EOL.
pub fn Document::insert(
self : Document,
section : String,
key : String,
value : String,
) -> Result[Document, Diagnostic] {
if !editable(self) {
return Err(edit_problem("EDIT001", "repair syntax errors before editing"))
}
if !valid_names(section, key, self.dialect) {
return Err(
edit_problem("EDIT005", "section or key cannot be represented safely"),
)
}
if !self.entries(section, key).is_empty() {
return Err(edit_problem("EDIT006", "key already exists; use set or set_at"))
}
let mut index = self.source.length()
let mut found = section == ""
let mut inside = section == ""
for node in self.nodes {
if node.kind == Section {
if inside {
index = node.span.start
inside = false
}
if same_name(node.section, section, self.dialect) {
found = true
inside = true
index = self.source.length()
}
}
}
let eol = newline_for(self)
let mut added = ""
if index > 0 &&
self.source[index - 1] != '\n' &&
self.source[index - 1] != '\r' &&
!(index == 1 && self.source[0] == '\uFEFF') {
added += eol
}
if !found {
added += "[" + section + "]" + eol
}
added += key + "=" + encode_value(value, "", self.dialect) + eol
let next = parse(
text(self.source, 0, index) +
added +
text(self.source, index, self.source.length()),
dialect=self.dialect,
limits=self.limits,
)
if !editable(next) || next.get(section, key) != Some(value) {
return Err(
edit_problem("EDIT003", "insertion failed parse-back verification"),
)
}
Ok(next)
}