char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
75a6060
main
1use serde_fjson::{ Document , PathSegment , parse}; 2use serde_json:: json; 3 4# [ test ] 5fn replacing_a_leaf_preserves_every_other_byte_and_quote_style () { 6let source = 7"// head\r\nimports: { 'de\\u0070': /* choice */ 'old' /* reason */ },\r\nother: 1e3\r\n" ; 8let mut document =Document :: parse ( source). unwrap (); 9 document 10. set ( & [ "imports" . into (), "dep" . into ()], & json! ( "it's new\nš" )) 11. unwrap (); 12assert_eq! ( 13 document. source (), 14 source. replace ( "'old'" , "'it\\'s new\\nš'" ) 15); 16assert_eq! ( 17 document. to_value (). unwrap ()[ "imports" ][ "dep" ], 18"it's new\nš" 19); 20assert_eq! ( 21& document. source ()[ document. root (). span . clone ()], 22 document. source ()[ source. find ( "imports" ). unwrap () ..]. trim_end_matches ([ '\r' , '\n' ]) 23); 24} 25 26# [ test ] 27fn unchanged_values_do_not_reformat_or_remove_internal_comments () { 28let source ="items: [1, /* why */ 2,]\nname: 'same'\n" ; 29let mut document =Document :: parse ( source). unwrap (); 30 document. set ( & [ "items" . into ()], & json! ([ 1 , 2 ])). unwrap (); 31 document. set ( & [ "name" . into ()], & json! ( "same" )). unwrap (); 32assert_eq! ( document. source (), source); 33} 34 35# [ test ] 36fn insertion_preserves_comments_line_endings_and_existing_layout () { 37for newlinein [ "\n" , "\r\n" , "\r" ] { 38let source =format! ( 39"// head{newline}options: {{{newline}\told: 'kept' // reason{newline}}}{newline}" 40); 41let mut document =Document :: parse ( & source). unwrap (); 42 document 43. set ( 44& [ "options" . into (), "new-key" . into ()], 45& json! ({ "enabled" : true }), 46) 47. unwrap (); 48assert! ( 49 document 50. source () 51. contains ( & format! ( "\told: 'kept' // reason{newline}" )) 52); 53assert! ( document. source (). contains ( & format! ( 54"\t\"new-key\": {{{newline}\t enabled: true{newline}\t}}" 55))); 56assert_eq! ( 57 document. to_value (). unwrap (), 58json! ({ "options" : { "old" : "kept" , "new-key" : { "enabled" : true }}}) 59); 60if newline !="\n" { 61assert! ( !document. source (). replace ( newline, "" ). contains ( '\n' )); 62} 63} 64for sourcein [ 65"" , 66"// eof" , 67"{/* empty */}" , 68"/* heading */\n" , 69"{\n // empty\n}" , 70] { 71let mut document =Document :: parse ( source). unwrap (); 72 document 73. set ( & [ "ę„ę¬čŖ" . into ()], & json! ([ true , false ])) 74. unwrap (); 75assert_eq! ( 76 document. to_value (). unwrap (), 77json! ({ "ę„ę¬čŖ" : [ true , false ]}) 78); 79for commentin [ "// eof" , "/* empty */" , "/* heading */" , "// empty" ] { 80if source. contains ( comment) { 81assert! ( document. source (). contains ( comment)); 82} 83} 84} 85} 86 87# [ test ] 88fn array_appends_and_sequential_edits_use_updated_paths () { 89let mut document = 90Document :: parse ( "list: [/* first */ 1, // note\n 2,]\nother: 'keep'" ). unwrap (); 91 document 92. set ( & [ "list" . into (), 2 . into ()], & json! ({ "name" : "new" })) 93. unwrap (); 94 document. remove ( & [ "list" . into (), 0 . into ()]). unwrap (); 95 document 96. set ( & [ "list" . into (), 1 . into (), "name" . into ()], & json! ( "updated" )) 97. unwrap (); 98assert_eq! ( 99 document. to_value (). unwrap (), 100json! ({ "list" : [ 2 , { "name" : "updated" }], "other" : "keep" }) 101); 102for keptin [ "/* first */" , "// note" , "other: 'keep'" ] { 103assert! ( document. source (). contains ( kept)); 104} 105} 106 107# [ test ] 108fn removing_a_member_keeps_external_comments_but_deletes_its_contents () { 109let source ="// head\na /* key : š */ : /* before */ { nested: [/* inside */ 1] } /* after , */ , // line\nb: 'kept'" ; 110let mut document =Document :: parse ( source). unwrap (); 111assert! ( document. remove ( & [ "a" . into ()]). unwrap ()); 112assert_eq! ( document. to_value (). unwrap (), json! ({ "b" : "kept" })); 113for commentin [ 114"// head" , 115"/* key : š */" , 116"/* before */" , 117"/* after , */" , 118"// line" , 119] { 120assert! ( document. source (). contains ( comment), "{comment}" ); 121} 122assert! ( !document. source (). contains ( "/* inside */" )); 123assert! ( document. source (). ends_with ( "b: 'kept'" )); 124assert! ( !document. remove ( & [ "a" . into ()]). unwrap ()); 125} 126 127# [ test ] 128fn removing_a_member_alone_on_its_line_takes_the_whole_line () { 129let source = 130"tasks: {\n a: {\n command: \"x\",\n },\n b: {\n command: \"y\",\n },\n},\n" ; 131let mut document =Document :: parse ( source). unwrap (); 132 document. remove ( & [ "tasks" . into (), "a" . into ()]). unwrap (); 133assert_eq! ( 134 document. source (), 135"tasks: {\n b: {\n command: \"y\",\n },\n},\n" 136); 137let mut document =Document :: parse ( source). unwrap (); 138 document. remove ( & [ "tasks" . into (), "b" . into ()]). unwrap (); 139assert_eq! ( 140 document. source (), 141"tasks: {\n a: {\n command: \"x\",\n },\n},\n" 142); 143for newlinein [ "\r\n" , "\r" ] { 144let source =format! ( "a: 1{newline} b: 2{newline}c: 3{newline}" ); 145let mut document =Document :: parse ( & source). unwrap (); 146 document. remove ( & [ "b" . into ()]). unwrap (); 147assert_eq! ( document. source (), format! ( "a: 1{newline}c: 3{newline}" )); 148} 149let mut document =Document :: parse ( "[\n 1,\n 2,\n 3\n]" ). unwrap (); 150 document. remove ( & [ 1 . into ()]). unwrap (); 151assert_eq! ( document. source (), "[\n 1,\n 3\n]" ); 152} 153 154# [ test ] 155fn repeated_removal_and_reinsertion_does_not_accumulate_whitespace () { 156let source = 157"tasks: {\n a: {\n command: \"x\",\n },\n b: {\n command: \"y\",\n },\n},\n" ; 158let mut document =Document :: parse ( source). unwrap (); 159for roundin 0 ..3 { 160 document. remove ( & [ "tasks" . into (), "a" . into ()]). unwrap (); 161 document. remove ( & [ "tasks" . into (), "b" . into ()]). unwrap (); 162 document 163. set ( 164& [ "tasks" . into (), "a" . into ()], 165& json! ({ "command" : format! ( "x{round}" )}), 166) 167. unwrap (); 168 document 169. set ( & [ "tasks" . into (), "b" . into ()], & json! ({ "command" : "y" })) 170. unwrap (); 171} 172assert! ( 173 !document 174. source () 175. lines () 176. any ( |line| !line. is_empty () && line. trim (). is_empty ()), 177"{}" , 178 document. source () 179); 180assert_eq! ( 181 document. to_value (). unwrap ()[ "tasks" ][ "a" ][ "command" ], 182json! ( "x2" ) 183); 184} 185 186# [ test ] 187fn removing_an_inline_member_keeps_its_line_and_comments () { 188let mut document =Document :: parse ( "tasks: { a: 1, b: 2 }, // done\n" ). unwrap (); 189 document. remove ( & [ "tasks" . into (), "a" . into ()]). unwrap (); 190assert_eq! ( document. source (), "tasks: { b: 2 }, // done\n" ); 191assert_eq! ( document. to_value (). unwrap (), json! ({ "tasks" : { "b" : 2 }})); 192} 193 194# [ test ] 195fn removal_handles_all_separator_styles_and_positions () { 196for separatorin [ "," , ", " , "\n" , " /* gap , š */\n" , ", // gap\n" , "\r\n" ] { 197for trailingin [ "" , "," , "\n" , ",\n" ] { 198for bracedin [ false , true ] { 199for indexin 0 ..3 { 200let source =format! ( "a: 1{separator}b: 2{separator}c: 3{trailing}" ); 201let source =if braced{ 202format! ( "{{{source}}}" ) 203} else { 204 source 205}; 206let mut document =Document :: parse ( source). unwrap (); 207let keys =[ "a" , "b" , "c" ]; 208assert! ( document. remove ( & [ keys[ index]. into ()]). unwrap ()); 209let mut expected =json! ({ "a" : 1 , "b" : 2 , "c" : 3 }); 210 expected. as_object_mut (). unwrap (). remove ( keys[ index]); 211assert_eq! ( 212 document. to_value (). unwrap (), 213 expected, 214"{}" , 215 document. source () 216); 217for keyin keys{ 218 document. remove ( & [ key. into ()]). unwrap (); 219} 220assert_eq! ( document. to_value (). unwrap (), json! ({})); 221 document. set ( & [ "new" . into ()], & json! ( true )). unwrap (); 222assert_eq! ( parse ( document. source ()). unwrap (), json! ({ "new" : true })); 223} 224} 225for indexin 0 ..3 { 226let mut document = 227Document :: parse ( format! ( "[1{separator}2{separator}3{trailing}]" )). unwrap (); 228 document. remove ( & [ index. into ()]). unwrap (); 229let mut expected =vec! [ 1 , 2 , 3 ]; 230 expected. remove ( index); 231assert_eq! ( document. to_value (). unwrap (), json! ( expected)); 232 document. remove ( & [ 0 . into ()]). unwrap (); 233 document. remove ( & [ 0 . into ()]). unwrap (); 234assert_eq! ( document. to_value (). unwrap (), json! ([])); 235 document. set ( & [ 0 . into ()], & json! ( "again" )). unwrap (); 236assert_eq! ( document. to_value (). unwrap (), json! ([ "again" ])); 237} 238} 239} 240} 241 242# [ test ] 243fn replacing_a_container_removes_only_comments_inside_it () { 244let source ="keep: 1\nobject: /* before */ {x: 1 /* inside */} // after\n" ; 245let mut document =Document :: parse ( source). unwrap (); 246 document. set ( & [ "object" . into ()], & json! ( false )). unwrap (); 247assert_eq! ( 248 document. source (), 249"keep: 1\nobject: /* before */ false // after\n" 250); 251let mut document =Document :: parse ( "// head\n[/* inside */ 1] // tail" ). unwrap (); 252 document. set ( & [], & json! ({ "new" : true })). unwrap (); 253assert_eq! ( document. source (), "// head\n{\n new: true\n} // tail" ); 254} 255 256# [ test ] 257fn root_replacements_preserve_leading_and_trailing_trivia_without_becoming_comments () { 258for sourcein [ 259"" , 260" " , 261"// heading" , 262" // heading" , 263"/* heading */" , 264"// heading\n" , 265"// heading\n{} // tail" , 266"// heading\n[1] // tail" , 267"// heading\nfalse // tail" , 268"// heading\nold: true" , 269"// heading\nold: true // tail\n" , 270"// heading\nold: true,\n// tail\n" , 271] { 272for valuein [ 273json! ( true ), 274json! ( null), 275json! ( 42 ), 276json! ( "new" ), 277json! ({ "new" : true }), 278json! ([]), 279json! ([ 1 , 2 ]), 280] { 281let mut document =Document :: parse ( source). unwrap (); 282 document. set ( & [], & value). unwrap (); 283assert_eq! ( document. to_value (). unwrap (), value, "{}" , document. source ()); 284for keptin [ "// heading" , "/* heading */" , "// tail" ] { 285if source. contains ( kept) { 286assert! ( document. source (). contains ( kept)); 287} 288} 289} 290} 291} 292 293# [ test ] 294fn implicit_root_replacements_preserve_trailing_trivia_and_remove_only_a_real_comma () { 295for newlinein [ "\n" , "\r" , "\r\n" ] { 296let prefix =format! ( "// heading{newline}" ); 297let suffix = 298format! ( " /* outside, */{newline}// before comma,{newline}, // after comma{newline}" ); 299let mut document =Document :: parse ( format! ( "{prefix}a: 1{suffix}" )). unwrap (); 300let without_comma = suffix. replace ( ", // after comma" , " // after comma" ); 301for valuein [ json! ({ "b" : 2 }), json! ([ 1 , 2 ]), json! ( false ), json! ({})] { 302 document. set ( & [], & value). unwrap (); 303assert_eq! ( document. to_value (). unwrap (), value); 304assert! ( document. source (). starts_with ( & prefix)); 305assert! ( document. source (). ends_with ( & without_comma)); 306} 307} 308} 309 310# [ test ] 311fn removing_the_last_implicit_member_preserves_all_external_comments () { 312let source ="a /* key */ : /* before */ { x: 1 /* inside */ } /* after, */ , // tail\n" ; 313let mut document =Document :: parse ( source). unwrap (); 314 document. remove ( & [ "a" . into ()]). unwrap (); 315assert_eq! ( document. to_value (). unwrap (), json! ({})); 316for commentin [ "/* key */" , "/* before */" , "/* after, */" , "// tail" ] { 317assert! ( document. source (). contains ( comment)); 318} 319assert! ( !document. source (). contains ( "/* inside */" )); 320 document. set ( & [ "b" . into ()], & json! ( 2 )). unwrap (); 321assert_eq! ( document. to_value (). unwrap (), json! ({ "b" : 2 })); 322} 323 324# [ test ] 325fn object_documents_require_object_roots_and_keep_missing_colon_diagnostics () { 326assert_eq! ( 327Document :: parse_object ( "imports {}" ) 328. unwrap_err () 329. render ( "config" , "imports {}" ), 330"config:1:9: Expected `:`" 331); 332for sourcein [ "false" , "1" , "[]" , "'a'" ] { 333assert! ( Document :: parse_object ( source). is_err ()); 334} 335let mut document =Document :: parse_object ( "a: 1" ). unwrap (); 336 document. set ( & [], & json! ( 5 )). unwrap (); 337assert_eq! ( document. to_value (). unwrap (), json! ( 5 )); 338} 339 340# [ test ] 341fn invalid_paths_and_unparseable_edits_are_atomic () { 342let source ="items: [1]\nscalar: false" ; 343let mut document =Document :: parse ( source). unwrap (); 344for pathin [ 345vec! [ "missing" . into (), "child" . into ()], 346vec! [ "items" . into (), 2 . into ()], 347vec! [ "items" . into (), "wrong" . into ()], 348vec! [ "scalar" . into (), "child" . into ()], 349vec! [ PathSegment :: Index ( 0 )], 350] { 351assert! ( document. set ( & path, & json! ( true )). is_err ()); 352assert_eq! ( document. source (), source); 353} 354assert! ( document. remove ( & []). is_err ()); 355assert_eq! ( document. source (), source); 356let mut nested =json! ( null); 357for _in 0 ..130 { 358 nested =json! ([ nested]); 359} 360let error = document. set ( & [ "items" . into ()], & nested). unwrap_err (); 361 error. render ( "test" , document. source ()); 362assert_eq! ( document. source (), source); 363}