use crate::lex::SyntaxKind;
use crate::yaml::{Document, SyntaxNode};
use rowan::ast::AstNode;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Rewrite {
pub before: String,
pub after: String,
}
pub fn to_yaml_1_2(doc: &Document) -> Vec<Rewrite> {
let mut rewrites = Vec::new();
rewrite_in(doc.syntax(), &mut rewrites);
rewrites
}
fn rewrite_in(node: &SyntaxNode, rewrites: &mut Vec<Rewrite>) {
for child in node.children() {
if child.kind() == SyntaxKind::SCALAR {
if let Some((after, kind)) = replacement_for(&child) {
let before = child.text().to_string();
let count = child.children_with_tokens().count();
child.splice_children(
0..count,
vec![crate::nodes::fresh_token(kind, &after).into()],
);
rewrites.push(Rewrite { before, after });
}
continue;
}
rewrite_in(&child, rewrites);
}
}
fn replacement_for(scalar: &SyntaxNode) -> Option<(String, SyntaxKind)> {
let mut tokens = scalar.children_with_tokens();
let token = tokens.next()?.into_token()?;
if tokens.next().is_some() {
return None;
}
if !matches!(
token.kind(),
SyntaxKind::INT | SyntaxKind::STRING | SyntaxKind::BOOL
) {
return None;
}
let text = token.text();
if text.starts_with(['"', '\'']) {
return None;
}
if scalar
.parent()
.is_some_and(|p| p.kind() == SyntaxKind::TAGGED_NODE)
{
return None;
}
if crate::ScalarValue::is_legacy_octal(text) {
let sign = if text.starts_with('-') { "-" } else { "" };
let digits = text.strip_prefix(['+', '-']).unwrap_or(text);
i64::from_str_radix(digits, 8).ok()?;
return Some((
format!("{sign}0o{}", digits.trim_start_matches('0')),
SyntaxKind::INT,
));
}
match text {
"yes" | "Yes" | "YES" | "on" | "On" | "ON" => Some(("true".into(), SyntaxKind::BOOL)),
"no" | "No" | "NO" | "off" | "Off" | "OFF" => Some(("false".into(), SyntaxKind::BOOL)),
_ => None,
}
}