@@ -353,39 +353,47 @@ fn apply_complex_replace(
353353 let start_byte = feature. location . byte_span . 0 - ws_len;
354354 let end_byte = feature. location . byte_span . 1 ;
355355
356- // Find the colon separating key from value
357- let colon_pos = find_key_colon ( content_with_ws) ;
356+ // Use query_exact to locate the value's byte span independently.
357+ // This avoids string-searching for the colon separator, which breaks
358+ // on quoted keys containing colons (e.g. "http://example.com": 8080).
359+ let value_feature = doc
360+ . query_exact ( route)
361+ . map_err ( |e| format ! ( "Query failed: {e}" ) ) ?;
358362
359- let key_part = match colon_pos {
360- Some ( pos) => {
361- let key = & content_with_ws[ ..pos + 1 ] ; // through the colon
362- key. to_string ( )
363+ let key_part = match value_feature {
364+ Some ( vf) => {
365+ let prefix = source[ start_byte..vf. location . byte_span . 0 ] . trim_end ( ) ;
366+ if prefix. is_empty ( ) {
367+ // Bare value (e.g. sequence item) — no key prefix
368+ let serialized = serde_yaml:: to_string ( value)
369+ . map_err ( |e| format ! ( "Failed to serialize YAML: {e}" ) ) ?;
370+ let trimmed = serialized. trim_end_matches ( '\n' ) ;
371+
372+ let line_start = source[ ..feature. location . byte_span . 0 ]
373+ . rfind ( '\n' )
374+ . map ( |nl| nl + 1 )
375+ . unwrap_or ( 0 ) ;
376+ let base_indent = feature. location . byte_span . 0 - line_start;
377+ let indent_str = " " . repeat ( base_indent) ;
378+
379+ let indented = indent_block ( trimmed, & indent_str) ;
380+
381+ let mut result = source. to_string ( ) ;
382+ result. replace_range (
383+ feature. location . byte_span . 0 ..feature. location . byte_span . 1 ,
384+ & indented,
385+ ) ;
386+ if !result. ends_with ( '\n' ) {
387+ result. push ( '\n' ) ;
388+ }
389+ return yamlpath:: Document :: new ( result)
390+ . map_err ( |e| format ! ( "Failed to re-parse YAML: {e}" ) ) ;
391+ }
392+ prefix. to_string ( )
363393 }
364394 None => {
365- // No colon found — bare value (e.g. sequence item)
366- let serialized = serde_yaml:: to_string ( value)
367- . map_err ( |e| format ! ( "Failed to serialize YAML: {e}" ) ) ?;
368- let trimmed = serialized. trim_end_matches ( '\n' ) ;
369-
370- let line_start = source[ ..feature. location . byte_span . 0 ]
371- . rfind ( '\n' )
372- . map ( |nl| nl + 1 )
373- . unwrap_or ( 0 ) ;
374- let base_indent = feature. location . byte_span . 0 - line_start;
375- let indent_str = " " . repeat ( base_indent) ;
376-
377- let indented = indent_block ( trimmed, & indent_str) ;
378-
379- let mut result = source. to_string ( ) ;
380- result. replace_range (
381- feature. location . byte_span . 0 ..feature. location . byte_span . 1 ,
382- & indented,
383- ) ;
384- if !result. ends_with ( '\n' ) {
385- result. push ( '\n' ) ;
386- }
387- return yamlpath:: Document :: new ( result)
388- . map_err ( |e| format ! ( "Failed to re-parse YAML: {e}" ) ) ;
395+ // Absent value (e.g. `key:\n`) — content is just key+colon
396+ content_with_ws. trim_end ( ) . to_string ( )
389397 }
390398 } ;
391399
@@ -424,16 +432,6 @@ fn apply_complex_replace(
424432 yamlpath:: Document :: new ( result) . map_err ( |e| format ! ( "Failed to re-parse YAML: {e}" ) )
425433}
426434
427- /// Find the first colon (key-value separator) in a YAML fragment.
428- ///
429- /// Uses a naive `find(':')`, consistent with yamlpatch's own Replace
430- /// implementation. This means colons inside quoted keys will be
431- /// misidentified — a known yamlpatch limitation that will be fixed
432- /// uniformly when yamlpatch addresses it.
433- fn find_key_colon ( content : & str ) -> Option < usize > {
434- content. find ( ':' )
435- }
436-
437435fn indent_block ( content : & str , indent : & str ) -> String {
438436 let mut result = String :: new ( ) ;
439437 for ( i, line) in content. lines ( ) . enumerate ( ) {
0 commit comments