///|
fn roman_rewrite_edit_id(index : Int) -> String {
"rewrite-\{index}"
}
///|
fn roman_lint_edit_id(index : Int) -> String {
"lint-\{index}"
}
///|
fn roman_lint_edit_reason(code : DiagnosticCode) -> RomanEditReason {
match code {
NonCanonicalCode => RomanCanonicalizationEdit
UppercaseRequiredCode | LowercaseRequiredCode | LowercaseNotAllowedCode =>
RomanCaseNormalizationEdit
UnicodeSourceNotAllowedCode => RomanUnicodeExpansionEdit
_ => RomanLintDiagnosticEdit(code)
}
}
///|
fn first_roman_lint_diagnostic_code(lint : RomanLintReport) -> DiagnosticCode? {
if lint.diagnostics.length() == 0 {
None
} else {
Some(lint.diagnostics[0].code)
}
}
///|
fn roman_lint_numeric_value(lint : RomanLintReport) -> Int? {
match lint.analysis.status {
AnalysisAccepted(report) => Some(report.value)
AnalysisRejected(_) => None
}
}
///|
fn parsed_roman_numeric_value(input : String, config : ParseConfig) -> Int? {
match parse_with_config(input, config) {
Ok(report) => Some(report.value)
Err(NonCanonicalRoman(expected)) =>
match parse_with_config(expected, config) {
Ok(report) => Some(report.value)
Err(_) => None
}
_ => None
}
}
///|
fn recover_rejected_roman_numeric_value(
rejection : RomanDocumentRejectedCandidate,
policy : RomanLintPolicy,
) -> Int? {
parsed_roman_numeric_value(rejection.source_text, {
..policy.parse_config,
accept_lowercase: true,
accept_unicode: true,
trim_outer_whitespace: false,
})
}
///|
fn format_complete_roman_lint_target(
value : Int,
policy : RomanLintPolicy,
) -> String? {
match
format_with_config(value, {
mode: policy.parse_config.mode,
letter_case: lint_output_case(policy.case_requirement),
}) {
Ok(replacement) => Some(replacement)
Err(_) => None
}
}
///|
fn conforming_roman_lint_replacement(
source : String,
replacement : String,
policy : RomanLintPolicy,
) -> String? {
if replacement == source {
return None
}
match lint_roman_document(replacement, policy) {
Ok(document) => {
let replacement_length = replacement.to_array().length()
if document.conforms &&
document.candidates.length() == 1 &&
document.rejected_candidates.length() == 0 &&
document.candidates[0].span == { start: 0, end: replacement_length } &&
document.candidates[0].source_text == replacement &&
document.candidates[0].lint.conforms {
Some(replacement)
} else {
None
}
}
Err(_) => None
}
}
///|
fn identify_roman_lint_edits(
edits : Array[RomanTextEdit],
) -> Array[RomanTextEdit] {
let identified : Array[RomanTextEdit] = []
for edit in sort_roman_edits_by_source(edits) {
identified.push({ ..edit, id: roman_lint_edit_id(identified.length()) })
}
identified
}
///|
/// Build a validated plan from replacements retained by a rewrite report.
pub fn roman_edit_plan_from_rewrite(
report : RomanRewriteReport,
rewrite_config : RewriteConfig,
edit_config : RomanEditPlanConfig,
) -> Result[RomanEditPlan, RomanEditError] {
let trusted = match rewrite_roman_text(report.original, rewrite_config) {
Ok(trusted) => trusted
Err(error) => return Err(RomanEditRewriteFailed(error))
}
let edits : Array[RomanTextEdit] = []
for replacement in trusted.replacements {
if replacement.source_text != replacement.replacement_text {
edits.push({
id: roman_rewrite_edit_id(edits.length()),
span: replacement.source_span,
expected_source: replacement.source_text,
replacement: replacement.replacement_text,
numeric_value: Some(replacement.value),
reason: RomanCanonicalizationEdit,
source_diagnostic_code: None,
})
}
}
validate_roman_edit_plan(trusted.original, edits, edit_config)
}
///|
/// Build a validated plan from deterministic document-lint replacements.
pub fn roman_edit_plan_from_document_lint(
report : RomanDocumentLintReport,
config : RomanEditPlanConfig,
) -> Result[RomanEditPlan, RomanEditError] {
let trusted = match lint_roman_document(report.original, report.policy) {
Ok(trusted) => trusted
Err(error) => return Err(RomanEditDocumentLintFailed(error))
}
let edits : Array[RomanTextEdit] = []
for candidate in trusted.candidates {
match
(
candidate.lint.replacement,
first_roman_lint_diagnostic_code(candidate.lint),
) {
(Some(proposed), Some(diagnostic_code)) =>
match
conforming_roman_lint_replacement(
candidate.source_text,
proposed,
trusted.policy,
) {
Some(replacement) =>
edits.push({
id: "candidate-\{edits.length()}",
span: candidate.span,
expected_source: candidate.source_text,
replacement,
numeric_value: roman_lint_numeric_value(candidate.lint),
reason: roman_lint_edit_reason(diagnostic_code),
source_diagnostic_code: Some(diagnostic_code),
})
None => ()
}
_ => ()
}
}
for rejection in trusted.rejected_candidates {
match recover_rejected_roman_numeric_value(rejection, trusted.policy) {
Some(value) =>
match format_complete_roman_lint_target(value, trusted.policy) {
Some(proposed) =>
match
conforming_roman_lint_replacement(
rejection.source_text,
proposed,
trusted.policy,
) {
Some(replacement) => {
let code = rejection.diagnostic.code
edits.push({
id: "rejected-\{edits.length()}",
span: rejection.span,
expected_source: rejection.source_text,
replacement,
numeric_value: Some(value),
reason: roman_lint_edit_reason(code),
source_diagnostic_code: Some(code),
})
}
None => ()
}
None => ()
}
None => ()
}
}
match
validate_roman_edit_plan(
trusted.original,
identify_roman_lint_edits(edits),
config,
) {
Err(error) => Err(error)
Ok(plan) => Ok({ ..plan, diagnostics: trusted.diagnostics })
}
}