@@ -95,6 +95,23 @@ def text(node: Node) -> str:
9595 return node .text .decode ("utf-8" )
9696
9797
98+ def _normalize_quotes (literal : str ) -> str :
99+ """Normalize a CFEngine string literal to the preferred quote style.
100+
101+ Double quotes are the default. A single-quoted literal is rewritten with
102+ double quotes unless its content contains a double-quote character, in
103+ which case it is left single-quoted to avoid escaping. An escaped single
104+ quote (\\ ') becomes a plain single quote when converting, since it needs
105+ no escaping inside a double-quoted string.
106+ """
107+ if len (literal ) < 2 or literal [0 ] != "'" or literal [- 1 ] != "'" :
108+ return literal
109+ inner = literal [1 :- 1 ]
110+ if '"' in inner :
111+ return literal
112+ return '"' + inner .replace ("\\ '" , "'" ) + '"'
113+
114+
98115class Formatter :
99116 """Accumulates formatted output line-by-line into a string buffer."""
100117
@@ -202,6 +219,8 @@ def stringify_single_line_nodes(nodes: list[Node]) -> str:
202219
203220def stringify_single_line_node (node : Node ) -> str :
204221 """Recursively flatten a node and its children into a single-line string."""
222+ if node .type == "quoted_string" :
223+ return _normalize_quotes (text (node ))
205224 if not node .children :
206225 return text (node )
207226 return stringify_single_line_nodes (node .children )
@@ -462,7 +481,7 @@ def _promiser_text(children: list[Node]) -> str | None:
462481 promiser_node = next ((c for c in children if c .type == "promiser" ), None )
463482 if not promiser_node :
464483 return None
465- return text (promiser_node )
484+ return _normalize_quotes ( text (promiser_node ) )
466485
467486
468487def _promiser_line_with_stakeholder (children : list [Node ]) -> str | None :
0 commit comments