tests/test.sh exit code (0 → resolved); the classification below is post-hoc and cannot change it.pwd && rg --files
Chunk ID: 618673 Wall time: 0.0000 seconds Process exited with code 0 Original token count: 21295 Output: Total output lines: 880 /app UPGRADING.md idna/Cargo.toml idna/src/make_uts46_mapping_table.py idna/src/uts46.rs idna/src/IdnaMappingTable.txt idna/src/lib.rs idna/src/punycode.rs idna/src/uts46_mapping_table.rs idna/tests/IdnaTestV2.txt idna/tests/uts46.rs idna/tests/punycode_tests.json idna/tests/tests.rs idna/tests/unit.rs idna/tests/punycode.rs idna/benches/all.rs LICENSE-APACHE data-url/Cargo.toml data-url/src/forgiving_base64.rs data-url/src/mime.rs data-url/src/lib.rs data-url/src/make_base64_decode_table.py data-url/tests/data-urls.json data-url/tests/generated-mime-types.json data-url/tests/base64.json data-url/tests/mime-types.json data-url/tests/wpt.rs data-url/README.md debug_metadata/url.natvis debug_metadata/README.md url/Cargo.toml url/src/host.rs url/src/slicing.rs url/src/origin.rs url/src/quirks.rs url/src/lib.rs url/src/path_segments.rs url/src/parser.rs url/tests/data.rs url/tests/setters_tests.json url/tests/urltestdata.json url/tests/unit.rs url/tests/debugger_visualizer.rs url/benches/parse_url.rs url/fuzz/Cargo.toml url/fuzz/fuzz_targets/parse.rs deny.toml LICENSE-MIT form_urlencoded/LICENSE-APACHE form_urlencoded/LICENSE-MIT form_urlencoded/Cargo.toml form_urlencoded/src/lib.rs SECURITY.md Cargo.lock percent_encoding/Cargo.toml percent_encoding/src/lib.rs Cargo.toml README.md target/debug/libpercent_encoding.rlib target/debug/liburl.rlib target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm.lock target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/8gogjfidpjzej9k6cuay2faf8.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/7ut7t8re1b826btusc5cl4v2x.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/agj3f7qghevfxfr23sr28wexi.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/a15dh6016y9308jgv5e47riq3.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/1kxqi2cpw1wo5d2dnndxvf6q3.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/e4mnmm3s16tqamgbh0pqntiok.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/91l4rjlvxicdixq97uw7o70v7.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/02xsfoa6kzjvu1nw0xxho3nlc.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/czcyo95aiasa68gs2u0oxokxg.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/biwxx8v5me9yij2twxhq1ogap.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/bykpo9u62mbxeguwentguhvb0.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/3aei1d9abwc04gi9rd1cvlj2z.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/46oe1q2uzd5akxevv3r99fzip.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/3t3b1o26rvpnv84xk4nmojy1e.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/deq16zjekzu8qul67ogjv8y31.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/9q2hld4yimftwjnk562e1bqew.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/2s6f3kdxqpgxfxyn1rc4xddur.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/dep-graph.bin target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/2775hsj8wkvgpksif3rqwm63w.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/9tkdo65ae4h7m9v8v0ats5of2.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/agq6p4fnfvqm77rbelsw0i4j6.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/work-products.bin target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/5v2gape4c61vc1ddu1kmo158v.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/28zb8h3ry0uxso7421eb3nt2k.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/epqw3gouk3odnxiuj1jo6hldl.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/bowd06t3jgepd1eidi3j9o54c.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/eekib1ye0fmfgef6oigd601yv.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/9whyp654sppd8k0rv4m5xmp82.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/01n2rtdvib90e2jsthfw51efv.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/900gb2yw76l4nrjafed0vh42m.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/a4enno5fp9euzckooxod3npn4.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/eczojs28z19hh99k6eb0g5j04.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/3soable5b906do9pka3x2j9ur.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/9g6iwsh5jtq52d8cdlz8yh4d1.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/3k910szc0dn0ty4qgva8y2qq8.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/4shhir26nlxxq5pss0w47by52.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/258jnbd9zwu7ew57q7u0253aq.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/cnhr8x0pnp7ugoh3ko1u8qs45.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/ee5cxl6a6evr6lsdy0cpxedow.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/3sebt2r12dsn1uk781qs3rnhj.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/bvlmdykgj4xwvt127xupf70rc.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/9phsq551fyex9j1tmbwzj74vr.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/502vo5unbup48b7ifgx6kehad.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/8rohcv8v1088qjcgbuymfy91d.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/0jbs3vebrmgnlpwe60ls7p9uz.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/9b7c6cf9hhtirf46fm6ecyjza.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/amkb38yht8dmwu1xgq03mti1h.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/de3ip26i8gt65m5od9658kn7r.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/2at9epbxsgpyahlfumu7gjq9i.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/899ugj7od0x8px3v3ujykw1p6.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/83ku5vaflgndgid98zhn9tawa.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/5ajzztpqhrapukd0uwkqipsna.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/1fn1lz061zk82vsndz5wj90o3.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/2qgczw6toiaw1fowj9gsqadxt.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/1mqw1yppynw397arbfl65hnkc.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/1ngss9k430ri6t12wx4v9zvcv.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/c7q5qfhogfqcw1j47ilyzo29x.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/csq4p3al5mzznuslat36pej80.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/a801ynq5m11ijqsdjkejvnu0s.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/98ekh76ol0t5gco2kc98r1jcy.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/bva2kc5y142ksiw58kco1di5h.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/cdzafb4i2ma0gvcjzdxxuifs2.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/6dty9q33gde7ehdujwsipdfj4.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/84sswellw3rrji8t4na9w2sdz.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/bgdylkdpa0do687xtaeak4iyx.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/dadh94qizpyp8wrvsgtcggzlp.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/query-cache.bin target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/3e03aiigsd27mupc64rh449kt.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/d4la7jajgpuc26d6ovpq23u1i.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/2ngb3hc1z6qtb06dq5cf3b3gd.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/4meummhxone2t5amg7j24wr07.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/3en08tqxbksbxhaswgqi0sx28.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/9jih05jud875hiz8en3na0y9j.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/dw4jde1xo1n0w2xa4jntnkut0.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/99lgqd3gkfdf8sehcdf25lcgr.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/6thfx2yz1zd3kkquq26m5mo9s.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/86qduwl8ldk65mozdb3lwi113.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/3clpzgo1jabo9s68qdd232jqb.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/4wxyd8q63l2nsrutm7rkca580.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/77wc4bkdzcxbswmtmuaeya94v.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/6m027519o46e4nno9lrqokkpz.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/7gyyqs9cor3i9yfyf4poizv7a.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/3r59joa5o2hnz85fgblg17ylz.o target/debug/incremental/unit-1rtx6ttyceut6/s-hjou0rzxf7-0gprirm-8mmu68kplntmgv5jkthjsnd6n/6adv5em2rxewf6n5fqd8d7m9d.o target/debug/incremental/url-3624ita5ju63y/s-hjou0ruvk4-1a7xc8r-0i4hmo3dzf6r3t8anyfcx7eg7/dep-graph.bin target/debug/incremental/url-3624ita5ju63y/s-hjou0ruvk4-1a7xc8r-0i4hmo3dzf6r3t8anyfcx7eg7/3sfk52cnzdq3lqldqdla1qavt.o target/debug/incremental/url-3624ita5ju63y/s-hjou0ruvk4-1a7xc8r-0i4hmo3dzf6r3t8anyfcx7eg7/work-products.bin target/debug/incremental/url-3624ita5ju63y/s-hjou0ruvk4-1a7xc8r-0i4hmo3dzf6r3t8anyfcx7eg7/8ilh8nwuzeqny6ljn8jbw2i8k.o target/debug/incremental/url-3624ita5ju63y/s-hjou0ruvk4-1a7xc8r-0i4hmo3dzf6r3t8anyfcx7eg7/4l1vm0j7jhztplafysuoeo49c.o target/debug/incremental/url-3624ita5ju63y/s-hjou0ruvk4-1a7xc8r-0i4hmo3dzf6r3t8anyfcx7eg7/5fd3u3mwmlmcw5h6ml7cnji74.o target/debug/incremental/url-3624ita5ju63y/s-hjou0ruvk4-1a7xc8r-0i4hmo3dzf6r3t8anyfcx7eg7/cz3povjitv5meffkbljpcs39v.o target/debug/incremental/url-3624ita5ju63y/s-hjou0ruvk4-1a7xc8r-0i4hmo3dzf6r3t8anyfcx7eg7/query-cache.bin target/debug/incremental/url-3624ita5ju63y/s-hjou0ruvk4-1a7xc8r-0i4hmo3dzf6r3t8anyfcx7eg7/7nwf44ir5vg1i4bia652z894r.o target/debug/incremental/url-3624ita5ju63y/s-hjou0ruvk4-1a7xc8r.lock target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/7cf9q8hxgewy7fxi1c17o9i05.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/9phx5yraspeaqwxoti1fgnsrn.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/04o9cgx8g2kz956ty9tpsgryb.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/5xo60wnqxg5d0zouu0oozzgbk.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/cb9bhzmp64hcwz0i5a2gb7ljo.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/4jj7m0xmf338g0h9ddeavq5tx.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/dep-graph.bin target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/5xerj1fbrybp04odmprzc2tli.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/work-products.bin target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/29coxn26xu0ucq3jceu8oc66m.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/92bdiyw3s2e5mvu7lwkgu6ivr.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/6s6poscwjxwdt5eyd6ym2r70v.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/7sk2a8sic80511hlwuz5hm29c.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/crxgtrgwuh1dtj2ptf8anhy4f.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/clrbht8a7rpj3ovzul6pwl080.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/4zoqbyoi6510ruh1s7ytygswe.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/3ym2vudmm4uwsebtisb6w517c.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/d4bdaewn9b8y4h2bd93722p17.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/afl8quyys9iwv7z3pvotj3q3e.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/emfetx9zqbl9xkryzrm9rd6dy.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/andomrbsvoso3335e5zhcj1yd.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/2n5wgpdggu3o4ey2pwhf1oujx.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/4mu0gd8b6clv6meqa7tzsdrcm.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/cpax00wsrg6ufv6h0tjjsk9ye.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/query-cache.bin target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/8gp07z2d6oe9s1xrjunbtmxij.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/7fu1fneukssvukuu62i6rlgw7.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/by2di4qiusbvp2t42y12c5clj.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/1s91xfp48o921nrv7o90ulx77.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/cqgmz89vl46h02iy47mi6p6c8.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/1lu2c6mxvsput39c4shrvgd2t.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/a887fs7l9anok7yw5qm1tt9wu.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/afn5x3bhw0mdk58dr58txotre.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/9ejwdbt8hw620lzueki5mdy2r.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417-dxiy9q2gpkp9di0h18y73gfd7/878cwrgtzo3vcpcqv4kutl8x3.o target/debug/incremental/unit-31a5kslhf18xu/s-hjou0rfi84-1h7v417.lock target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/9othkraw3qbdb5e9ev5eg5rtq.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/cwa0bj1vggn6xjpkf9nhbt9nt.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/cyujqk6vivgcm8twqp2o31aet.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/24pnjujsglbnpa144imdgykot.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/7twjxl7vvtzau3v8q21uk6x9u.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/5x0z02og4td8p1q4ek7ionbpk.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/cvjal1ffqe3tvi7ugt2tr7s24.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/dep-graph.bin target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/2le4kr196w91zykrbz1u38ldy.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/1zkurdy0ajj5ckboo5dkzjdp5.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/work-products.bin target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/27tkn0dgl56qtoxj7edcfivqp.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/e1at3tiumu08k4o2rzvebe97t.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/3nibda4ow4wvc5gijmqhxw72y.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/9vuil794wxxxynqcxqujejul8.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/0u7qi5ddthljzjcpkfc6ta8tr.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/f18ebq8c8rpbwvkctv3cv57sk.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/ai50witipr45wh5lk17f12bdv.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/6dio3aosr8nplwcwufgppoyhd.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/87igb8wed1g5rk6i2ov4tpsrx.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/5dborrn4nvyw8a2bcp2hs7ixt.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/4n2lprkjg52owqvtd3cs0bm1h.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/query-cache.bin target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/budy2lspdszs5dw7y7iww3mg2.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/el8785gtcnf25tu6awe8kr1wy.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/7wkr8rbqzz5uqwno7nh6bql9j.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/eoqjw13y8h2cc0l8sptxpkgou.o target/debug/incremental/idna-2s4mps90pq4mr/s-hjou0rrsg9-0nrlnq3-0ng4nc93r1nsvdzi5bfrz1y8b/9dvyata0eq51m9lrvm2pikibw.o target/debug/increm…11295 tokens truncated…na-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/7egkk04k0cwqbmpk14n54zkbd.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/1w9ggip3r5yvw1ib0lszj7q5q.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/cpd3lf7bfub5o2zpcnj8ms62f.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/83n5zanjqh7u7cdpbpfbylw5d.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/bskett978cnufg3oa1dkn384t.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/dhqgdrxu1wyggalthhbq5ea55.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/dus9hxw1qovzexfbulw8gsgjk.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/b1esjaqdlxbgf2ygwawyaeuaa.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/elz60d1tgq5mxnhd26z7n3si1.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/bqq6dziwi1j5l7dvvqmxqkd1z.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/eh2dcw1tp8oje3nwi5vwb5lad.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/6oogwtkt0i6bmq9u9gplsyaeg.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/42dvma5wqliejr82eyzb2q69w.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/dep-graph.bin target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/374vie5vh0duu1ajn3rnnkfib.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/4jf22m847t81rab5bz84pe5df.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/2dfl0gnco0sc40368c7e20r0r.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/47vol74196wo0j6tquo28jkqh.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/2azj6kiibt5eyokraia0d07q5.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/d14l2ixyjpvk6wdbgvlpimc7n.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/445ncjwk0z08t6g0tbhl29h8m.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/work-products.bin target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/5f1ombqj49vnoomh36cayufa6.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/b2lipmztvegkf1teunq2uj0ao.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/4kdv0bvdwq9gw0f1iw2ex3xhl.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/cyjbtppw3jh4zq1y83dacp1ja.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/8cna0rkcfwkxilm86rds2066t.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/9akcqwg56im4wy0pi3t06r9sv.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/6ugab35o9yqleyreat18b6666.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/ew4nas5lp3gxypvmxmldvbfqu.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/1eyk6frfkd66zloqnqp8fni92.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/e9iwac2wb9smqkg5uwiw6g4yg.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/4pz8qrtlta9z2a597pdwjii9g.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/8sb7n6m3oyk8gfawcp6da5kpf.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/bh8twggzos86tmwav6u8g66r6.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/23icqbbqtb6fh1vg0j2nucrdl.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/49874fpap0bz9ev8wt0oh3jst.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/bcoxddjebok2dxnd5sqmj9vf0.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/64c3wkk340p79p1poyski6azn.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/4noqoa6vh5sk0tt8xgqqbvsm5.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/7u1ap92hc0rjbzokmg35g66f8.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/9r7z0js5b605u71e9osf253jy.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/9o0wf07ejkokkolxhhqyse963.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/acm5nyx4m5n7dq7ohx5zvt4aw.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/4lr2ldlx67yk15v8x340dgu90.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/40gd9wpt86fycn4fgyvix592l.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/61ise8tyush68jvuafwxg7tc2.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/9fzopt7i74e3d2hvsv9ayiu67.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/33ewk0x0fixd108ujewwes8xm.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/6bw77pie55u142ljxsob6dp7d.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/17ipzdtk2bfoc2hihkjlfagtn.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/0uqiempuze711xd3n7g6qowld.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/8y44fbxqsl3hw52ir2jgauam9.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/df1d1d6crlbw7skdgc3ra10mk.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/a93155414jj8u3eea9nlhbjz7.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/5u6j1gxxe61aliwm527klnujn.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/1ycfj2jkxq2cv1jkk554tvlih.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/8o05of94ry9hcqubi0igstt4o.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/bmb7llbmjd5v81iwnszfbvq4o.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/3me9m97na6kimdm6mlhkhr9d4.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/4whcma8tc866ma6ncilkczxbc.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/duwjxu5aefho4hz3di6w04uwh.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/query-cache.bin target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/docrj24u4cc71i69idv1gwvg2.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/53m6w1lqbwsu0ti7wzsh9j0wx.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/8efl2q7pm8bc1qe4le1o61j7f.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/7db4aa24j8eocrz5lyrfj0nrw.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/d6uyc7se8s4xqmsosofikoug0.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/bx4juq239lz1f2o9qd9boce6a.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/8m003xfbyn8who1bk030hdjj0.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/0otxrwmc96bn4uvt0dwss9f5z.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/06m80elx0jwoqg06t0fuif9m8.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/dtyuzookd6p0q32oh0d9easrb.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/6dl4gd0nauy0h0fluun719tyy.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8-4lvrqk2ogf09c5qvlidvae53n/4v4x7nrekfjjhm0e5355r9on3.o target/debug/incremental/idna-3i1sdojlmq5rx/s-hjou0p06fa-1bn0oh8.lock target/debug/build/libc-ad25a1021d8bc503/build_script_build-ad25a1021d8bc503 target/debug/build/libc-ad25a1021d8bc503/build_script_build-ad25a1021d8bc503.d target/debug/build/libc-ad25a1021d8bc503/build-script-build target/debug/build/serde_core-58ec2e75b87c45d5/build_script_build-58ec2e75b87c45d5.d target/debug/build/serde_core-58ec2e75b87c45d5/build-script-build target/debug/build/serde_core-58ec2e75b87c45d5/build_script_build-58ec2e75b87c45d5 target/debug/build/proc-macro2-4ccecbdd481395e7/build_script_build-4ccecbdd481395e7 target/debug/build/proc-macro2-4ccecbdd481395e7/build_script_build-4ccecbdd481395e7.d target/debug/build/proc-macro2-4ccecbdd481395e7/build-script-build target/debug/build/syn-543e65d50dac7c38/output target/debug/build/syn-543e65d50dac7c38/invoked.timestamp target/debug/build/syn-543e65d50dac7c38/stderr target/debug/build/syn-543e65d50dac7c38/root-output target/debug/build/serde_core-e64bb9c45bfe80f3/out/private.rs target/debug/build/serde_core-e64bb9c45bfe80f3/output target/debug/build/serde_core-e64bb9c45bfe80f3/invoked.timestamp target/debug/build/serde_core-e64bb9c45bfe80f3/stderr target/debug/build/serde_core-e64bb9c45bfe80f3/root-output target/debug/build/proc-macro2-bd986aab9ef403a2/output target/debug/build/proc-macro2-bd986aab9ef403a2/invoked.timestamp target/debug/build/proc-macro2-bd986aab9ef403a2/stderr target/debug/build/proc-macro2-bd986aab9ef403a2/root-output target/debug/build/zmij-f4c55d12a4111926/output target/debug/build/zmij-f4c55d12a4111926/invoked.timestamp target/debug/build/zmij-f4c55d12a4111926/stderr target/debug/build/zmij-f4c55d12a4111926/root-output target/debug/build/anyhow-865c62892f774c58/build_script_build-865c62892f774c58.d target/debug/build/anyhow-865c62892f774c58/build_script_build-865c62892f774c58 target/debug/build/anyhow-865c62892f774c58/build-script-build target/debug/build/anyhow-7ef9f9965b442394/output target/debug/build/anyhow-7ef9f9965b442394/invoked.timestamp target/debug/build/anyhow-7ef9f9965b442394/stderr target/debug/build/anyhow-7ef9f9965b442394/root-output target/debug/build/libc-6f42403205f9303e/output target/debug/build/libc-6f42403205f9303e/invoked.timestamp target/debug/build/libc-6f42403205f9303e/stderr target/debug/build/libc-6f42403205f9303e/root-output target/debug/build/zmij-66703644318e2170/build_script_build-66703644318e2170 target/debug/build/zmij-66703644318e2170/build_script_build-66703644318e2170.d target/debug/build/zmij-66703644318e2170/build-script-build target/debug/libdata_url.rlib target/debug/build/serde_json-0ccae1069ea496b1/build_script_build-0ccae1069ea496b1.d target/debug/build/serde_json-0ccae1069ea496b1/build_script_build-0ccae1069ea496b1 target/debug/build/serde_json-0ccae1069ea496b1/build-script-build target/debug/libform_urlencoded.rlib target/debug/libform_urlencoded.d target/debug/libpercent_encoding.d target/debug/libidna.rlib target/debug/liburl.d target/debug/libdata_url.d target/debug/libidna.d target/debug/build/serde_json-56a19a2a331ed41b/output target/debug/build/serde_json-56a19a2a331ed41b/invoked.timestamp target/debug/build/serde_json-56a19a2a331ed41b/stderr target/debug/build/serde_json-56a19a2a331ed41b/root-output target/debug/build/serde-ef19f8e495c8dd30/out/private.rs target/debug/build/serde-ef19f8e495c8dd30/output target/debug/build/serde-ef19f8e495c8dd30/invoked.timestamp target/debug/build/serde-ef19f8e495c8dd30/stderr target/debug/build/serde-ef19f8e495c8dd30/root-output target/debug/build/serde-b1128fd7a9b46781/build_script_build-b1128fd7a9b46781 target/debug/build/serde-b1128fd7a9b46781/build-script-build target/debug/build/serde-b1128fd7a9b46781/build_script_build-b1128fd7a9b46781.d target/debug/build/quote-5b51a8dd8290005e/build_script_build-5b51a8dd8290005e target/debug/build/quote-5b51a8dd8290005e/build_script_build-5b51a8dd8290005e.d target/debug/build/quote-5b51a8dd8290005e/build-script-build target/debug/build/quote-c8e2688b757dd7d5/output target/debug/build/quote-c8e2688b757dd7d5/invoked.timestamp target/debug/build/quote-c8e2688b757dd7d5/stderr target/debug/build/quote-c8e2688b757dd7d5/root-output target/debug/deps/libdebugger_test_parser-1e0be53005f01159.rmeta target/debug/deps/url-a827b1398b422d9c target/debug/deps/liblog-33dc065ff7e289ee.rlib target/debug/deps/libdirs_sys_next-a1b4df44aae05101.rmeta target/debug/deps/libserde-d508bb3ff3dd4055.rlib target/debug/deps/libserde_json-dc770969e002a827.rlib target/debug/deps/libassert_matches-997c872328890f4a.rlib target/debug/deps/libdebugger_test-0ad1fd381eb6ea52.so target/debug/deps/log-33dc065ff7e289ee.d target/debug/deps/libunicode_width-efcc0ccd9d13bff1.rlib target/debug/deps/libcfg_if-cfacf702a0ce2975.rlib target/debug/deps/wpt-606068d5db7783da.d target/debug/deps/debugger_test_parser-1e0be53005f01159.d target/debug/deps/libgetopts-75a5cfb030da81b5.rlib target/debug/deps/tests-59cd81bff71ae601 target/debug/deps/quote-01f83d3eea7b6f2b.d target/debug/deps/libregex-b4101d53f0aa179b.rmeta target/debug/deps/libproc_macro2-1eef835a9c881aa4.rlib target/debug/deps/libitoa-07607aacafe8c110.rmeta target/debug/deps/libregex_syntax-bd872110d85f2b72.rmeta target/debug/deps/idna-a04547c9c0564051.d target/debug/deps/unit-49bdfe7cb4758172 target/debug/deps/libzmij-5cc8b833226b94c4.rmeta target/debug/deps/percent_encoding-63ddf0be2a9952ea.d target/debug/deps/libaho_corasick-2e9dc80e9d7bfbbd.rmeta target/debug/deps/serde-d508bb3ff3dd4055.d target/debug/deps/liblibc-ace0f0bbdcbf0373.rlib target/debug/deps/libregex_automata-64dbae0cf587aa72.rmeta target/debug/deps/zmij-5cc8b833226b94c4.d target/debug/deps/libdirs_sys_next-a1b4df44aae05101.rlib target/debug/deps/url-a827b1398b422d9c.d target/debug/deps/libunicode_width-efcc0ccd9d13bff1.rmeta target/debug/deps/libdebugger_test_parser-1e0be53005f01159.rlib target/debug/deps/libmemchr-0e59f4b81d97fa7d.rlib target/debug/deps/serde_derive-966e8ca013c020f8.d target/debug/deps/libterm-218f25e3d530c86d.rmeta target/debug/deps/libterm-218f25e3d530c86d.rlib target/debug/deps/dirs_next-9f9ae5649cd83b0e.d target/debug/deps/bencher-99ba437b5f01bd5b.d target/debug/deps/libdirs_next-9f9ae5649cd83b0e.rmeta target/debug/deps/libcfg_if-cfacf702a0ce2975.rmeta target/debug/deps/libregex_syntax-bd872110d85f2b72.rlib target/debug/deps/libregex-b4101d53f0aa179b.rlib target/debug/deps/libmemchr-0e59f4b81d97fa7d.rmeta target/debug/deps/libproc_macro2-1eef835a9c881aa4.rmeta target/debug/deps/regex_syntax-bd872110d85f2b72.d target/debug/deps/cfg_if-cfacf702a0ce2975.d target/debug/deps/libbencher-99ba437b5f01bd5b.rmeta target/debug/deps/libserde_core-b1dddf678d28d575.rmeta target/debug/deps/itoa-07607aacafe8c110.d target/debug/deps/unit-a5735195d8f8601f.d target/debug/deps/libbencher-99ba437b5f01bd5b.rlib target/debug/deps/syn-e03837b46b547978.d target/debug/deps/libzmij-5cc8b833226b94c4.rlib target/debug/deps/unicode_width-efcc0ccd9d13bff1.d target/debug/deps/libsyn-729eba90f722dded.rmeta target/debug/deps/percent_encoding-63ddf0be2a9952ea target/debug/deps/aho_corasick-2e9dc80e9d7bfbbd.d target/debug/deps/memchr-0e59f4b81d97fa7d.d target/debug/deps/idna-a04547c9c0564051 target/debug/deps/libtester-9eb23c65da3afe94.rlib target/debug/deps/libquote-01f83d3eea7b6f2b.rmeta target/debug/deps/libgetopts-75a5cfb030da81b5.rmeta target/debug/deps/libtester-9eb23c65da3afe94.rmeta target/debug/deps/syn-729eba90f722dded.d target/debug/deps/libserde-d508bb3ff3dd4055.rmeta target/debug/deps/liblog-33dc065ff7e289ee.rmeta target/debug/deps/dirs_sys_next-a1b4df44aae05101.d target/debug/deps/num_cpus-b2a4ede6cfed6774.d target/debug/deps/data-7f47dce1bd8e2e0f target/debug/deps/libassert_matches-997c872328890f4a.rmeta target/debug/deps/libserde_json-dc770969e002a827.rmeta target/debug/deps/libserde_derive-966e8ca013c020f8.so target/debug/deps/wpt-606068d5db7783da target/debug/deps/libitoa-07607aacafe8c110.rlib target/debug/deps/libanyhow-258208b6d6d01432.rlib target/debug/deps/libregex_automata-64dbae0cf587aa72.rlib target/debug/deps/libserde_core-b1dddf678d28d575.rlib target/debug/deps/unit-a5735195d8f8601f target/debug/deps/unicode_ident-3febed1ec838496d.d target/debug/deps/libsyn-e03837b46b547978.rmeta target/debug/deps/debugger_test-0ad1fd381eb6ea52.d target/debug/deps/libquote-01f83d3eea7b6f2b.rlib target/debug/deps/regex-b4101d53f0aa179b.d target/debug/deps/assert_matches-997c872328890f4a.d target/debug/deps/regex_automata-64dbae0cf587aa72.d target/debug/deps/libunicode_ident-3febed1ec838496d.rlib target/debug/deps/liblibc-ace0f0bbdcbf0373.rmeta target/debug/deps/proc_macro2-1eef835a9c881aa4.d target/debug/deps/libnum_cpus-b2a4ede6cfed6774.rmeta target/debug/deps/libunicode_ident-3febed1ec838496d.rmeta target/debug/deps/tester-9eb23c65da3afe94.d target/debug/deps/libaho_corasick-2e9dc80e9d7bfbbd.rlib target/debug/deps/libsyn-729eba90f722dded.rlib target/debug/deps/getopts-75a5cfb030da81b5.d target/debug/deps/anyhow-258208b6d6d01432.d target/debug/deps/data-7f47dce1bd8e2e0f.d target/debug/deps/libc-ace0f0bbdcbf0373.d target/debug/deps/serde_json-dc770969e002a827.d target/debug/deps/libanyhow-258208b6d6d01432.rmeta target/debug/deps/serde_core-b1dddf678d28d575.d target/debug/deps/term-218f25e3d530c86d.d target/debug/deps/tests-59cd81bff71ae601.d target/debug/deps/libsyn-e03837b46b547978.rlib target/debug/deps/unit-49bdfe7cb4758172.d target/debug/deps/libdirs_next-9f9ae5649cd83b0e.rlib target/debug/deps/libnum_cpus-b2a4ede6cfed6774.rlib target/debug/deps/idna-726324058677286e.d target/debug/deps/form_urlencoded-95861b6439c1abb6.d target/debug/deps/libunicode_normalization-bbe37813a405e491.rmeta target/debug/deps/libtinyvec_macros-25017543a519123f.rlib target/debug/deps/url-d627b16b64164b50.d target/debug/deps/libtinyvec-a50bd56f6ee2ed68.rlib target/debug/deps/liburl-d627b16b64164b50.rlib target/debug/deps/libform_urlencoded-95861b6439c1abb6.rmeta target/debug/deps/percent_encoding-b82aaa83a3f1ece1.d target/debug/deps/unicode_bidi-d947e98f1314215b.d target/debug/deps/tinyvec_macros-25017543a519123f.d target/debug/deps/libunicode_bidi-d947e98f1314215b.rmeta target/debug/deps/libunicode_bidi-d947e98f1314215b.rlib target/debug/build/syn-255ecb636db8ec71/build_script_build-255ecb636db8ec71 target/debug/deps/libdata_url-d398adaa45d3869b.rlib target/debug/build/syn-255ecb636db8ec71/build-script-build target/debug/build/syn-255ecb636db8ec71/build_script_build-255ecb636db8ec71.d target/debug/deps/libform_urlencoded-95861b6439c1abb6.rlib target/debug/deps/libpercent_encoding-b82aaa83a3f1ece1.rlib target/debug/deps/libtinyvec-a50bd56f6ee2ed68.rmeta target/debug/deps/libtinyvec_macros-25017543a519123f.rmeta target/debug/deps/libidna-726324058677286e.rmeta target/debug/deps/data_url-d398adaa45d3869b.d target/debug/deps/unicode_normalization-bbe37813a405e491.d target/debug/deps/libidna-726324058677286e.rlib target/debug/deps/liburl-d627b16b64164b50.rmeta target/debug/deps/libunicode_normalization-bbe37813a405e491.rlib target/debug/deps/libdata_url-d398adaa45d3869b.rmeta target/debug/deps/tinyvec-a50bd56f6ee2ed68.d target/debug/deps/libpercent_encoding-b82aaa83a3f1ece1.rmeta target/CACHEDIR.TAG
git status --short
Chunk ID: 35618b Wall time: 0.0000 seconds Process exited with code 128 Original token count: 18 Output: fatal: not a git repository (or any of the parent directories): .git
rg "BeforePath|AfterPort|cannot-be-a-base|non-special|special" url/src -n
Chunk ID: 580c5d
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1621
Output:
url/src/host.rs:45: /// a special URL, or percent encoded for non-special URLs. Hosts for
url/src/host.rs:46: /// non-special URLs are also called opaque hosts.
url/src/slicing.rs:49:/// let authority: &str = &some_url[Position::BeforeUsername..Position::AfterPort];
url/src/slicing.rs:50:/// let data_url_payload: &str = &some_url[Position::BeforePath..Position::AfterQuery];
url/src/slicing.rs:91: AfterPort,
url/src/slicing.rs:92: BeforePath,
url/src/slicing.rs:152: Position::AfterPort => self.path_start as usize,
url/src/slicing.rs:154: Position::BeforePath => self.path_start as usize,
url/src/quirks.rs:143: &url[Position::BeforeHost..Position::AfterPort]
url/src/quirks.rs:149: // If context object’s url’s cannot-be-a-base-URL flag is set, then return.
url/src/quirks.rs:216: // Empty host on special not file url
url/src/quirks.rs:238: &url[Position::BeforePort..Position::AfterPort]
url/src/quirks.rs:277: || (SchemeType::from(url.scheme()).is_special()
url/src/quirks.rs:278: // \ is a segment delimiter for 'special' URLs"
url/src/lib.rs:46:assert!(&issue_list_url[Position::BeforePath..] == "/rust-lang/rust/issues?labels=E-easy&state=open");
url/src/lib.rs:54:Some URLs are said to be *cannot-be-a-base*:
url/src/lib.rs:660: if SchemeType::from(self.scheme()).is_special() {
url/src/lib.rs:819: /// or cannot-be-a-base like `data:text/plain,Stuff`.
url/src/lib.rs:851: /// of a special URL, or percent encoded for non-special URLs.
url/src/lib.rs:853: /// Ports are omitted if they match the well known port of a special URL.
url/src/lib.rs:889: /// Return whether this URL is a cannot-be-a-base URL,
url/src/lib.rs:1016: /// of a special URL, or percent encoded for non-special URLs.
url/src/lib.rs:1056: /// of a special URL, or percent encoded for non-special URLs.
url/src/lib.rs:1096: /// of a special URL, or percent encoded for non-special URLs.
url/src/lib.rs:1241: /// For cannot-be-a-base URLs, this is an arbitrary string that doesn’t start with '/'.
url/src/lib.rs:1272: /// Unless this URL is cannot-be-a-base,
url/src/lib.rs:1276: /// Return `None` for cannot-be-a-base URLs.
url/src/lib.rs:1656: /// Return `Err(())` if this URL is cannot-be-a-base.
url/src/lib.rs:1685: /// If this URL is cannot-be-a-base, does not have a host, or has the `file` scheme;
url/src/lib.rs:1723: /// Cannot set port for cannot-be-a-base URLs:
url/src/lib.rs:1832: /// Cannot remove host for 'special' schemes (e.g. `http`):
url/src/lib.rs:1848: /// Cannot change or remove host for cannot-be-a-base URLs:
url/src/lib.rs:1871: /// If this URL is cannot-be-a-base or there is an error parsing the given `host`,
url/src/lib.rs:1883: if host.is_empty() && scheme_type.is_special() && !scheme_type.is_file() {
url/src/lib.rs:1901: if SchemeType::from(self.scheme()).is_special() {
url/src/lib.rs:1907: if scheme_type.is_special() && !scheme_type.is_file() {
url/src/lib.rs:1984: /// If this URL is cannot-be-a-base, do nothing and return `Err`.
url/src/lib.rs:2035: /// If this URL is cannot-be-a-base or does not have a host, do nothing and return `Err`.
url/src/lib.rs:2119: /// If this URL is cannot-be-a-base or does not have a host, do nothing and return `Err`.
url/src/lib.rs:2209: /// * If this URL is cannot-be-a-base and the new scheme is one of
url/src/lib.rs:2269: /// Cannot change URL’s scheme from `mailto` (cannot-be-a-base) to `https`:
url/src/lib.rs:2320: // If url’s scheme is a special scheme and buffer is not a special scheme, then return.
url/src/lib.rs:2321: if (new_scheme_type.is_special() && !old_scheme_type.is_special()) ||
url/src/lib.rs:2322: // If url’s scheme is not a special scheme and buffer is a special scheme, then return.
url/src/lib.rs:2323: (!new_scheme_type.is_special() && old_scheme_type.is_special()) ||
url/src/lib.rs:2331: if !remaining.is_empty() || (!self.has_host() && new_scheme_type.is_special()) {
url/src/path_segments.rs:49: if SchemeType::from(url.scheme()).is_special() {
url/src/path_segments.rs:230: // Non special url's path might still be empty
url/src/parser.rs:39:// The backslash (\) character is treated as a path separator in special URLs
url/src/parser.rs:85: RelativeUrlWithCannotBeABaseBase => "relative URL with a cannot-be-a-base base",
url/src/parser.rs:86: SetHostOnCannotBeABaseUrl => "a cannot-be-a-base URL doesn’t have a host to set",
url/src/parser.rs:153: pub fn is_special(&self) -> bool {
url/src/parser.rs:432: // special relative or authority state
url/src/parser.rs:438: // "Cannot-be-a-base" URLs only happen with "not special" schemes.
url/src/parser.rs:444: // special authority slashes state
url/src/parser.rs:454: SchemeType::NotSpecial => self.parse_non_special(input, scheme_type, scheme_end),
url/src/parser.rs:459: fn parse_non_special(
url/src/parser.rs:795: // A special url always has a path.
url/src/parser.rs:798: && (SchemeType::from(base_url.scheme()).is_special() || !input.is_empty())
url/src/parser.rs:883: '\\' if scheme_type.is_special() => break,
url/src/parser.rs:893: // url is special and c is U+005C (\)
url/src/parser.rs:896: if c == '/' || c == '?' || c == '#' || (scheme_type.is_special() && c == '\\') {
url/src/parser.rs:953: if scheme_type.is_special() {
url/src/parser.rs:988: '\\' if scheme_type.is_special() => break,
url/src/parser.rs:1020: if !scheme_type.is_special() {
url/src/parser.rs:1135: // If url is special, then:
url/src/parser.rs:1136: if scheme_type.is_special() {
url/src/parser.rs:1141: // A special URL always has a non-empty path.
url/src/parser.rs:1191: && scheme_type.is_special() =>
url/src/parser.rs:1205: if scheme_type.is_special() {
url/src/parser.rs:1236: // and then if neither c is U+002F (/), nor url is special and c is U+005C (\), append the empty string to url’s path.
url/src/parser.rs:1242: // nor url is special and c is U+005C (\), append the empty string to url’s path.
url/src/parser.rs:1434: let set = if scheme_type.is_special() {
nl -ba url/src/lib.rs | sed -n '560,760p'
Chunk ID: bb6d8a
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2206
Output:
560 /// let url_str = "https://example.net/";
561 /// let url = Url::parse(url_str)?;
562 /// assert_eq!(url.as_str(), url_str);
563 /// # Ok(())
564 /// # }
565 /// # run().unwrap();
566 /// ```
567 #[inline]
568 pub fn as_str(&self) -> &str {
569 &self.serialization
570 }
571
572 /// Return the serialization of this URL.
573 ///
574 /// This consumes the `Url` and takes ownership of the `String` stored in it.
575 ///
576 /// # Examples
577 ///
578 /// ```rust
579 /// use url::Url;
580 /// # use url::ParseError;
581 ///
582 /// # fn run() -> Result<(), ParseError> {
583 /// let url_str = "https://example.net/";
584 /// let url = Url::parse(url_str)?;
585 /// assert_eq!(String::from(url), url_str);
586 /// # Ok(())
587 /// # }
588 /// # run().unwrap();
589 /// ```
590 #[inline]
591 #[deprecated(since = "2.3.0", note = "use Into<String>")]
592 pub fn into_string(self) -> String {
593 self.into()
594 }
595
596 /// For internal testing, not part of the public API.
597 ///
598 /// Methods of the `Url` struct assume a number of invariants.
599 /// This checks each of these invariants and panic if one is not met.
600 /// This is for testing rust-url itself.
601 #[doc(hidden)]
602 pub fn check_invariants(&self) -> Result<(), String> {
603 macro_rules! assert {
604 ($x: expr) => {
605 if !$x {
606 return Err(format!(
607 "!( {} ) for URL {:?}",
608 stringify!($x),
609 self.serialization
610 ));
611 }
612 };
613 }
614
615 macro_rules! assert_eq {
616 ($a: expr, $b: expr) => {
617 {
618 let a = $a;
619 let b = $b;
620 if a != b {
621 return Err(format!("{:?} != {:?} ({} != {}) for URL {:?}",
622 a, b, stringify!($a), stringify!($b),
623 self.serialization))
624 }
625 }
626 }
627 }
628
629 assert!(self.scheme_end >= 1);
630 assert!(self.byte_at(0).is_ascii_alphabetic());
631 assert!(self
632 .slice(1..self.scheme_end)
633 .chars()
634 .all(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '+' | '-' | '.')));
635 assert_eq!(self.byte_at(self.scheme_end), b':');
636
637 if self.slice(self.scheme_end + 1..).starts_with("//") {
638 // URL with authority
639 if self.username_end != self.serialization.len() as u32 {
640 match self.byte_at(self.username_end) {
641 b':' => {
642 assert!(self.host_start >= self.username_end + 2);
643 assert_eq!(self.byte_at(self.host_start - 1), b'@');
644 }
645 b'@' => assert!(self.host_start == self.username_end + 1),
646 _ => assert_eq!(self.username_end, self.scheme_end + 3),
647 }
648 }
649 assert!(self.host_start >= self.username_end);
650 assert!(self.host_end >= self.host_start);
651 let host_str = self.slice(self.host_start..self.host_end);
652 match self.host {
653 HostInternal::None => assert_eq!(host_str, ""),
654 HostInternal::Ipv4(address) => assert_eq!(host_str, address.to_string()),
655 HostInternal::Ipv6(address) => {
656 let h: Host<String> = Host::Ipv6(address);
657 assert_eq!(host_str, h.to_string())
658 }
659 HostInternal::Domain => {
660 if SchemeType::from(self.scheme()).is_special() {
661 assert!(!host_str.is_empty())
662 }
663 }
664 }
665 if self.path_start == self.host_end {
666 assert_eq!(self.port, None);
667 } else {
668 assert_eq!(self.byte_at(self.host_end), b':');
669 let port_str = self.slice(self.host_end + 1..self.path_start);
670 assert_eq!(
671 self.port,
672 Some(port_str.parse::<u16>().expect("Couldn't parse port?"))
673 );
674 }
675 assert!(
676 self.path_start as usize == self.serialization.len()
677 || matches!(self.byte_at(self.path_start), b'/' | b'#' | b'?')
678 );
679 } else {
680 // Anarchist URL (no authority)
681 assert_eq!(self.username_end, self.scheme_end + 1);
682 assert_eq!(self.host_start, self.scheme_end + 1);
683 assert_eq!(self.host_end, self.scheme_end + 1);
684 assert_eq!(self.host, HostInternal::None);
685 assert_eq!(self.port, None);
686 assert_eq!(self.path_start, self.scheme_end + 1);
687 }
688 if let Some(start) = self.query_start {
689 assert!(start >= self.path_start);
690 assert_eq!(self.byte_at(start), b'?');
691 }
692 if let Some(start) = self.fragment_start {
693 assert!(start >= self.path_start);
694 assert_eq!(self.byte_at(start), b'#');
695 }
696 if let (Some(query_start), Some(fragment_start)) = (self.query_start, self.fragment_start) {
697 assert!(fragment_start > query_start);
698 }
699
700 let other = Url::parse(self.as_str()).expect("Failed to parse myself?");
701 assert_eq!(&self.serialization, &other.serialization);
702 assert_eq!(self.scheme_end, other.scheme_end);
703 assert_eq!(self.username_end, other.username_end);
704 assert_eq!(self.host_start, other.host_start);
705 assert_eq!(self.host_end, other.host_end);
706 assert!(
707 self.host == other.host ||
708 // XXX No host round-trips to empty host.
709 // See https://[code-host]/whatwg/url/issues/79
710 (self.host_str(), other.host_str()) == (None, Some(""))
711 );
712 assert_eq!(self.port, other.port);
713 assert_eq!(self.path_start, other.path_start);
714 assert_eq!(self.query_start, other.query_start);
715 assert_eq!(self.fragment_start, other.fragment_start);
716 Ok(())
717 }
718
719 /// Return the origin of this URL (<https://url.spec.whatwg.org/#origin>)
720 ///
721 /// Note: this returns an opaque origin for `file:` URLs, which causes
722 /// `url.origin() != url.origin()`.
723 ///
724 /// # Examples
725 ///
726 /// URL with `ftp` scheme:
727 ///
728 /// ```rust
729 /// use url::{Host, Origin, Url};
730 /// # use url::ParseError;
731 ///
732 /// # fn run() -> Result<(), ParseError> {
733 /// let url = Url::parse("ftp://example.com/foo")?;
734 /// assert_eq!(url.origin(),
735 /// Origin::Tuple("ftp".into(),
736 /// Host::Domain("example.com".into()),
737 /// 21));
738 /// # Ok(())
739 /// # }
740 /// # run().unwrap();
741 /// ```
742 ///
743 /// URL with `blob` scheme:
744 ///
745 /// ```rust
746 /// use url::{Host, Origin, Url};
747 /// # use url::ParseError;
748 ///
749 /// # fn run() -> Result<(), ParseError> {
750 /// let url = Url::parse("blob:https://example.com/foo")?;
751 /// assert_eq!(url.origin(),
752 /// Origin::Tuple("https".into(),
753 /// Host::Domain("example.com".into()),
754 /// 443));
755 /// # Ok(())
756 /// # }
757 /// # run().unwrap();
758 /// ```
759 ///
760 /// URL with `file` scheme:
nl -ba url/src/lib.rs | sed -n '1200,1325p'
Chunk ID: 19820b
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1429
Output:
1200 /// # ;
1201 /// ```
1202 ///
1203 /// ```
1204 /// /// With application-specific known default port numbers
1205 /// fn socket_addrs(url: url::Url) -> std::io::Result<Vec<std::net::SocketAddr>> {
1206 /// url.socket_addrs(|| match url.scheme() {
1207 /// "socks5" | "socks5h" => Some(1080),
1208 /// _ => None,
1209 /// })
1210 /// }
1211 /// ```
1212 pub fn socket_addrs(
1213 &self,
1214 default_port_number: impl Fn() -> Option<u16>,
1215 ) -> io::Result<Vec<SocketAddr>> {
1216 // Note: trying to avoid the Vec allocation by returning `impl AsRef<[SocketAddr]>`
1217 // causes borrowck issues because the return value borrows `default_port_number`:
1218 //
1219 // https://[code-host]/rust-lang/rfcs/blob/master/text/1951-expand-impl-trait.md#scoping-for-type-and-lifetime-parameters
1220 //
1221 // > This RFC proposes that *all* type parameters are considered in scope
1222 // > for `impl Trait` in return position
1223
1224 fn io_result<T>(opt: Option<T>, message: &str) -> io::Result<T> {
1225 opt.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, message))
1226 }
1227
1228 let host = io_result(self.host(), "No host name in the URL")?;
1229 let port = io_result(
1230 self.port_or_known_default().or_else(default_port_number),
1231 "No port number in the URL",
1232 )?;
1233 Ok(match host {
1234 Host::Domain(domain) => (domain, port).to_socket_addrs()?.collect(),
1235 Host::Ipv4(ip) => vec![(ip, port).into()],
1236 Host::Ipv6(ip) => vec![(ip, port).into()],
1237 })
1238 }
1239
1240 /// Return the path for this URL, as a percent-encoded ASCII string.
1241 /// For cannot-be-a-base URLs, this is an arbitrary string that doesn’t start with '/'.
1242 /// For other URLs, this starts with a '/' slash
1243 /// and continues with slash-separated path segments.
1244 ///
1245 /// # Examples
1246 ///
1247 /// ```rust
1248 /// use url::{Url, ParseError};
1249 ///
1250 /// # fn run() -> Result<(), ParseError> {
1251 /// let url = Url::parse("https://example.com/api/versions?page=2")?;
1252 /// assert_eq!(url.path(), "/api/versions");
1253 ///
1254 /// let url = Url::parse("https://example.com")?;
1255 /// assert_eq!(url.path(), "/");
1256 ///
1257 /// let url = Url::parse("https://example.com/countries/việt nam")?;
1258 /// assert_eq!(url.path(), "/countries/vi%E1%BB%87t%20nam");
1259 /// # Ok(())
1260 /// # }
1261 /// # run().unwrap();
1262 /// ```
1263 pub fn path(&self) -> &str {
1264 match (self.query_start, self.fragment_start) {
1265 (None, None) => self.slice(self.path_start..),
1266 (Some(next_component_start), _) | (None, Some(next_component_start)) => {
1267 self.slice(self.path_start..next_component_start)
1268 }
1269 }
1270 }
1271
1272 /// Unless this URL is cannot-be-a-base,
1273 /// return an iterator of '/' slash-separated path segments,
1274 /// each as a percent-encoded ASCII string.
1275 ///
1276 /// Return `None` for cannot-be-a-base URLs.
1277 ///
1278 /// When `Some` is returned, the iterator always contains at least one string
1279 /// (which may be empty).
1280 ///
1281 /// # Examples
1282 ///
1283 /// ```
1284 /// use url::Url;
1285 /// # use std::error::Error;
1286 ///
1287 /// # fn run() -> Result<(), Box<dyn Error>> {
1288 /// let url = Url::parse("https://example.com/foo/bar")?;
1289 /// let mut path_segments = url.path_segments().ok_or_else(|| "cannot be base")?;
1290 /// assert_eq!(path_segments.next(), Some("foo"));
1291 /// assert_eq!(path_segments.next(), Some("bar"));
1292 /// assert_eq!(path_segments.next(), None);
1293 ///
1294 /// let url = Url::parse("https://example.com")?;
1295 /// let mut path_segments = url.path_segments().ok_or_else(|| "cannot be base")?;
1296 /// assert_eq!(path_segments.next(), Some(""));
1297 /// assert_eq!(path_segments.next(), None);
1298 ///
1299 /// let url = Url::parse("data:text/plain,HelloWorld")?;
1300 /// assert!(url.path_segments().is_none());
1301 ///
1302 /// let url = Url::parse("https://example.com/countries/việt nam")?;
1303 /// let mut path_segments = url.path_segments().ok_or_else(|| "cannot be base")?;
1304 /// assert_eq!(path_segments.next(), Some("countries"));
1305 /// assert_eq!(path_segments.next(), Some("vi%E1%BB%87t%20nam"));
1306 /// # Ok(())
1307 /// # }
1308 /// # run().unwrap();
1309 /// ```
1310 pub fn path_segments(&self) -> Option<str::Split<'_, char>> {
1311 let path = self.path();
1312 path.strip_prefix('/').map(|remainder| remainder.split('/'))
1313 }
1314
1315 /// Return this URL’s query string, if any, as a percent-encoded ASCII string.
1316 ///
1317 /// # Examples
1318 ///
1319 /// ```rust
1320 /// use url::Url;
1321 /// # use url::ParseError;
1322 ///
1323 /// fn run() -> Result<(), ParseError> {
1324 /// let url = Url::parse("https://example.com/products?page=2")?;
1325 /// let query = url.query();
nl -ba url/src/slicing.rs | sed -n '1,210p'
Chunk ID: 292f11
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1931
Output:
1 // Copyright 2016 The rust-url developers.
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8
9 use crate::Url;
10 use std::ops::{Index, Range, RangeFrom, RangeFull, RangeTo};
11
12 impl Index<RangeFull> for Url {
13 type Output = str;
14 fn index(&self, _: RangeFull) -> &str {
15 &self.serialization
16 }
17 }
18
19 impl Index<RangeFrom<Position>> for Url {
20 type Output = str;
21 fn index(&self, range: RangeFrom<Position>) -> &str {
22 &self.serialization[self.index(range.start)..]
23 }
24 }
25
26 impl Index<RangeTo<Position>> for Url {
27 type Output = str;
28 fn index(&self, range: RangeTo<Position>) -> &str {
29 &self.serialization[..self.index(range.end)]
30 }
31 }
32
33 impl Index<Range<Position>> for Url {
34 type Output = str;
35 fn index(&self, range: Range<Position>) -> &str {
36 &self.serialization[self.index(range.start)..self.index(range.end)]
37 }
38 }
39
40 /// Indicates a position within a URL based on its components.
41 ///
42 /// A range of positions can be used for slicing `Url`:
43 ///
44 /// ```rust
45 /// # use url::{Url, Position};
46 /// # fn something(some_url: Url) {
47 /// let serialization: &str = &some_url[..];
48 /// let serialization_without_fragment: &str = &some_url[..Position::AfterQuery];
49 /// let authority: &str = &some_url[Position::BeforeUsername..Position::AfterPort];
50 /// let data_url_payload: &str = &some_url[Position::BeforePath..Position::AfterQuery];
51 /// let scheme_relative: &str = &some_url[Position::BeforeUsername..];
52 /// # }
53 /// ```
54 ///
55 /// In a pseudo-grammar (where `[`…`]?` makes a sub-sequence optional),
56 /// URL components and delimiters that separate them are:
57 ///
58 /// ```notrust
59 /// url =
60 /// scheme ":"
61 /// [ "//" [ username [ ":" password ]? "@" ]? host [ ":" port ]? ]?
62 /// path [ "?" query ]? [ "#" fragment ]?
63 /// ```
64 ///
65 /// When a given component is not present,
66 /// its "before" and "after" position are the same
67 /// (so that `&some_url[BeforeFoo..AfterFoo]` is the empty string)
68 /// and component ordering is preserved
69 /// (so that a missing query "is between" a path and a fragment).
70 ///
71 /// The end of a component and the start of the next are either the same or separate
72 /// by a delimiter.
73 /// (Not that the initial `/` of a path is considered part of the path here, not a delimiter.)
74 /// For example, `&url[..BeforeFragment]` would include a `#` delimiter (if present in `url`),
75 /// so `&url[..AfterQuery]` might be desired instead.
76 ///
77 /// `BeforeScheme` and `AfterFragment` are always the start and end of the entire URL,
78 /// so `&url[BeforeScheme..X]` is the same as `&url[..X]`
79 /// and `&url[X..AfterFragment]` is the same as `&url[X..]`.
80 #[derive(Copy, Clone, Debug)]
81 pub enum Position {
82 BeforeScheme,
83 AfterScheme,
84 BeforeUsername,
85 AfterUsername,
86 BeforePassword,
87 AfterPassword,
88 BeforeHost,
89 AfterHost,
90 BeforePort,
91 AfterPort,
92 BeforePath,
93 AfterPath,
94 BeforeQuery,
95 AfterQuery,
96 BeforeFragment,
97 AfterFragment,
98 }
99
100 impl Url {
101 #[inline]
102 fn index(&self, position: Position) -> usize {
103 match position {
104 Position::BeforeScheme => 0,
105
106 Position::AfterScheme => self.scheme_end as usize,
107
108 Position::BeforeUsername => {
109 if self.has_authority() {
110 self.scheme_end as usize + "://".len()
111 } else {
112 debug_assert!(self.byte_at(self.scheme_end) == b':');
113 debug_assert!(self.scheme_end + ":".len() as u32 == self.username_end);
114 self.scheme_end as usize + ":".len()
115 }
116 }
117
118 Position::AfterUsername => self.username_end as usize,
119
120 Position::BeforePassword => {
121 if self.has_authority() && self.byte_at(self.username_end) == b':' {
122 self.username_end as usize + ":".len()
123 } else {
124 debug_assert!(self.username_end == self.host_start);
125 self.username_end as usize
126 }
127 }
128
129 Position::AfterPassword => {
130 if self.has_authority() && self.byte_at(self.username_end) == b':' {
131 debug_assert!(self.byte_at(self.host_start - "@".len() as u32) == b'@');
132 self.host_start as usize - "@".len()
133 } else {
134 debug_assert!(self.username_end == self.host_start);
135 self.host_start as usize
136 }
137 }
138
139 Position::BeforeHost => self.host_start as usize,
140
141 Position::AfterHost => self.host_end as usize,
142
143 Position::BeforePort => {
144 if self.port.is_some() {
145 debug_assert!(self.byte_at(self.host_end) == b':');
146 self.host_end as usize + ":".len()
147 } else {
148 self.host_end as usize
149 }
150 }
151
152 Position::AfterPort => self.path_start as usize,
153
154 Position::BeforePath => self.path_start as usize,
155
156 Position::AfterPath => match (self.query_start, self.fragment_start) {
157 (Some(q), _) => q as usize,
158 (None, Some(f)) => f as usize,
159 (None, None) => self.serialization.len(),
160 },
161
162 Position::BeforeQuery => match (self.query_start, self.fragment_start) {
163 (Some(q), _) => {
164 debug_assert!(self.byte_at(q) == b'?');
165 q as usize + "?".len()
166 }
167 (None, Some(f)) => f as usize,
168 (None, None) => self.serialization.len(),
169 },
170
171 Position::AfterQuery => match self.fragment_start {
172 None => self.serialization.len(),
173 Some(f) => f as usize,
174 },
175
176 Position::BeforeFragment => match self.fragment_start {
177 Some(f) => {
178 debug_assert!(self.byte_at(f) == b'#');
179 f as usize + "#".len()
180 }
181 None => self.serialization.len(),
182 },
183
184 Position::AfterFragment => self.serialization.len(),
185 }
186 }
187 }
nl -ba url/src/parser.rs | sed -n '430,560p'
Chunk ID: 50aea7
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1672
Output:
430 }
431 SchemeType::SpecialNotFile => {
432 // special relative or authority state
433 let (slashes_count, remaining) = input.count_matching(|c| matches!(c, '/' | '\\'));
434 if let Some(base_url) = self.base_url {
435 if slashes_count < 2
436 && base_url.scheme() == &self.serialization[..scheme_end as usize]
437 {
438 // "Cannot-be-a-base" URLs only happen with "not special" schemes.
439 debug_assert!(!base_url.cannot_be_a_base());
440 self.serialization.clear();
441 return self.parse_relative(input, scheme_type, base_url);
442 }
443 }
444 // special authority slashes state
445 self.log_violation_if(ExpectedDoubleSlash, || {
446 input
447 .clone()
448 .take_while(|&c| matches!(c, '/' | '\\'))
449 .collect::<String>()
450 != "//"
451 });
452 self.after_double_slash(remaining, scheme_type, scheme_end)
453 }
454 SchemeType::NotSpecial => self.parse_non_special(input, scheme_type, scheme_end),
455 }
456 }
457
458 /// Scheme other than file, http, https, ws, ws, ftp.
459 fn parse_non_special(
460 mut self,
461 input: Input<'_>,
462 scheme_type: SchemeType,
463 scheme_end: u32,
464 ) -> ParseResult<Url> {
465 // path or authority state (
466 if let Some(input) = input.split_prefix("//") {
467 return self.after_double_slash(input, scheme_type, scheme_end);
468 }
469 // Anarchist URL (no authority)
470 let path_start = to_u32(self.serialization.len())?;
471 let username_end = path_start;
472 let host_start = path_start;
473 let host_end = path_start;
474 let host = HostInternal::None;
475 let port = None;
476 let remaining = if let Some(input) = input.split_prefix('/') {
477 let path_start = self.serialization.len();
478 self.serialization.push('/');
479 self.parse_path(scheme_type, &mut false, path_start, input)
480 } else {
481 self.parse_cannot_be_a_base_path(input)
482 };
483 self.with_query_and_fragment(
484 scheme_type,
485 scheme_end,
486 username_end,
487 host_start,
488 host_end,
489 host,
490 port,
491 path_start,
492 remaining,
493 )
494 }
495
496 fn parse_file(
497 mut self,
498 input: Input<'_>,
499 scheme_type: SchemeType,
500 base_file_url: Option<&Url>,
501 ) -> ParseResult<Url> {
502 use crate::SyntaxViolation::Backslash;
503 // file state
504 debug_assert!(self.serialization.is_empty());
505 let (first_char, input_after_first_char) = input.split_first();
506 if matches!(first_char, Some('/') | Some('\\')) {
507 self.log_violation_if(SyntaxViolation::Backslash, || first_char == Some('\\'));
508 // file slash state
509 let (next_char, input_after_next_char) = input_after_first_char.split_first();
510 if matches!(next_char, Some('/') | Some('\\')) {
511 self.log_violation_if(Backslash, || next_char == Some('\\'));
512 // file host state
513 self.serialization.push_str("file://");
514 let scheme_end = "file".len() as u32;
515 let host_start = "file://".len() as u32;
516 let (path_start, mut host, remaining) =
517 self.parse_file_host(input_after_next_char)?;
518 let mut host_end = to_u32(self.serialization.len())?;
519 let mut has_host = !matches!(host, HostInternal::None);
520 let remaining = if path_start {
521 self.parse_path_start(SchemeType::File, &mut has_host, remaining)
522 } else {
523 let path_start = self.serialization.len();
524 self.serialization.push('/');
525 self.parse_path(SchemeType::File, &mut has_host, path_start, remaining)
526 };
527
528 // For file URLs that have a host and whose path starts
529 // with the windows drive letter we just remove the host.
530 if !has_host {
531 self.serialization
532 .drain(host_start as usize..host_end as usize);
533 host_end = host_start;
534 host = HostInternal::None;
535 }
536 let (query_start, fragment_start) =
537 self.parse_query_and_fragment(scheme_type, scheme_end, remaining)?;
538 return Ok(Url {
539 serialization: self.serialization,
540 scheme_end,
541 username_end: host_start,
542 host_start,
543 host_end,
544 host,
545 port: None,
546 path_start: host_end,
547 query_start,
548 fragment_start,
549 });
550 } else {
551 self.serialization.push_str("file://");
552 let scheme_end = "file".len() as u32;
553 let host_start = "file://".len();
554 let mut host_end = host_start;
555 let mut host = HostInternal::None;
556 if !starts_with_windows_drive_letter_segment(&input_after_first_char) {
557 if let Some(base_url) = base_file_url {
558 let first_segment = base_url.path_segments().unwrap().next().unwrap();
559 if is_normalized_windows_drive_letter(first_segment) {
560 self.serialization.push('/');
nl -ba url/src/parser.rs | sed -n '760,930p'
Chunk ID: 16d718
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2045
Output:
760 .push_str(base_url.slice(..scheme_end + 1));
761 if let Some(after_prefix) = input.split_prefix("//") {
762 return self.after_double_slash(after_prefix, scheme_type, scheme_end);
763 }
764 return self.after_double_slash(remaining, scheme_type, scheme_end);
765 }
766 let path_start = base_url.path_start;
767 self.serialization.push_str(base_url.slice(..path_start));
768 self.serialization.push('/');
769 let remaining = self.parse_path(
770 scheme_type,
771 &mut true,
772 path_start as usize,
773 input_after_first_char,
774 );
775 self.with_query_and_fragment(
776 scheme_type,
777 base_url.scheme_end,
778 base_url.username_end,
779 base_url.host_start,
780 base_url.host_end,
781 base_url.host,
782 base_url.port,
783 base_url.path_start,
784 remaining,
785 )
786 }
787 _ => {
788 let before_query = match (base_url.query_start, base_url.fragment_start) {
789 (None, None) => &*base_url.serialization,
790 (Some(i), _) | (None, Some(i)) => base_url.slice(..i),
791 };
792 self.serialization.push_str(before_query);
793 // FIXME spec says just "remove last entry", not the "pop" algorithm
794 self.pop_path(scheme_type, base_url.path_start as usize);
795 // A special url always has a path.
796 // A path always starts with '/'
797 if self.serialization.len() == base_url.path_start as usize
798 && (SchemeType::from(base_url.scheme()).is_special() || !input.is_empty())
799 {
800 self.serialization.push('/');
801 }
802 let remaining = match input.split_first() {
803 (Some('/'), remaining) => self.parse_path(
804 scheme_type,
805 &mut true,
806 base_url.path_start as usize,
807 remaining,
808 ),
809 _ => {
810 self.parse_path(scheme_type, &mut true, base_url.path_start as usize, input)
811 }
812 };
813 self.with_query_and_fragment(
814 scheme_type,
815 base_url.scheme_end,
816 base_url.username_end,
817 base_url.host_start,
818 base_url.host_end,
819 base_url.host,
820 base_url.port,
821 base_url.path_start,
822 remaining,
823 )
824 }
825 }
826 }
827
828 fn after_double_slash(
829 mut self,
830 input: Input<'_>,
831 scheme_type: SchemeType,
832 scheme_end: u32,
833 ) -> ParseResult<Url> {
834 self.serialization.push('/');
835 self.serialization.push('/');
836 // authority state
837 let before_authority = self.serialization.len();
838 let (username_end, remaining) = self.parse_userinfo(input, scheme_type)?;
839 let has_authority = before_authority != self.serialization.len();
840 // host state
841 let host_start = to_u32(self.serialization.len())?;
842 let (host_end, host, port, remaining) =
843 self.parse_host_and_port(remaining, scheme_end, scheme_type)?;
844 if host == HostInternal::None && has_authority {
845 return Err(ParseError::EmptyHost);
846 }
847 // path state
848 let path_start = to_u32(self.serialization.len())?;
849 let remaining = self.parse_path_start(scheme_type, &mut true, remaining);
850 self.with_query_and_fragment(
851 scheme_type,
852 scheme_end,
853 username_end,
854 host_start,
855 host_end,
856 host,
857 port,
858 path_start,
859 remaining,
860 )
861 }
862
863 /// Return (username_end, remaining)
864 fn parse_userinfo<'i>(
865 &mut self,
866 mut input: Input<'i>,
867 scheme_type: SchemeType,
868 ) -> ParseResult<(u32, Input<'i>)> {
869 let mut last_at = None;
870 let mut remaining = input.clone();
871 let mut char_count = 0;
872 while let Some(c) = remaining.next() {
873 match c {
874 '@' => {
875 if last_at.is_some() {
876 self.log_violation(SyntaxViolation::UnencodedAtSign)
877 } else {
878 self.log_violation(SyntaxViolation::EmbeddedCredentials)
879 }
880 last_at = Some((char_count, remaining.clone()))
881 }
882 '/' | '?' | '#' => break,
883 '\\' if scheme_type.is_special() => break,
884 _ => (),
885 }
886 char_count += 1;
887 }
888 let (mut userinfo_char_count, remaining) = match last_at {
889 None => return Ok((to_u32(self.serialization.len())?, input)),
890 Some((0, remaining)) => {
891 // Otherwise, if one of the following is true
892 // c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
893 // url is special and c is U+005C (\)
894 // If @ flag is set and buffer is the empty string, validation error, return failure.
895 if let (Some(c), _) = remaining.split_first() {
896 if c == '/' || c == '?' || c == '#' || (scheme_type.is_special() && c == '\\') {
897 return Err(ParseError::EmptyHost);
898 }
899 }
900 return Ok((to_u32(self.serialization.len())?, remaining));
901 }
902 Some(x) => x,
903 };
904
905 let mut username_end = None;
906 let mut has_password = false;
907 let mut has_username = false;
908 while userinfo_char_count > 0 {
909 let (c, utf8_c) = input.next_utf8().unwrap();
910 userinfo_char_count -= 1;
911 if c == ':' && username_end.is_none() {
912 // Start parsing password
913 username_end = Some(to_u32(self.serialization.len())?);
914 // We don't add a colon if the password is empty
915 if userinfo_char_count > 0 {
916 self.serialization.push(':');
917 has_password = true;
918 }
919 } else {
920 if !has_password {
921 has_username = true;
922 }
923 self.check_url_code_point(c, &input);
924 self.serialization
925 .extend(utf8_percent_encode(utf8_c, USERINFO));
926 }
927 }
928 let username_end = match username_end {
929 Some(i) => i,
930 None => to_u32(self.serialization.len())?,
nl -ba url/src/parser.rs | sed -n '930,1285p'
Chunk ID: b326b5
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 4203
Output:
930 None => to_u32(self.serialization.len())?,
931 };
932 if has_username || has_password {
933 self.serialization.push('@');
934 }
935 Ok((username_end, remaining))
936 }
937
938 fn parse_host_and_port<'i>(
939 &mut self,
940 input: Input<'i>,
941 scheme_end: u32,
942 scheme_type: SchemeType,
943 ) -> ParseResult<(u32, HostInternal, Option<u16>, Input<'i>)> {
944 let (host, remaining) = Parser::parse_host(input, scheme_type)?;
945 write!(&mut self.serialization, "{}", host).unwrap();
946 let host_end = to_u32(self.serialization.len())?;
947 if let Host::Domain(h) = &host {
948 if h.is_empty() {
949 // Port with an empty host
950 if remaining.starts_with(":") {
951 return Err(ParseError::EmptyHost);
952 }
953 if scheme_type.is_special() {
954 return Err(ParseError::EmptyHost);
955 }
956 }
957 };
958
959 let (port, remaining) = if let Some(remaining) = remaining.split_prefix(':') {
960 let scheme = || default_port(&self.serialization[..scheme_end as usize]);
961 Parser::parse_port(remaining, scheme, self.context)?
962 } else {
963 (None, remaining)
964 };
965 if let Some(port) = port {
966 write!(&mut self.serialization, ":{}", port).unwrap()
967 }
968 Ok((host_end, host.into(), port, remaining))
969 }
970
971 pub fn parse_host(
972 mut input: Input<'_>,
973 scheme_type: SchemeType,
974 ) -> ParseResult<(Host<String>, Input<'_>)> {
975 if scheme_type.is_file() {
976 return Parser::get_file_host(input);
977 }
978 // Undo the Input abstraction here to avoid allocating in the common case
979 // where the host part of the input does not contain any tab or newline
980 let input_str = input.chars.as_str();
981 let mut inside_square_brackets = false;
982 let mut has_ignored_chars = false;
983 let mut non_ignored_chars = 0;
984 let mut bytes = 0;
985 for c in input_str.chars() {
986 match c {
987 ':' if !inside_square_brackets => break,
988 '\\' if scheme_type.is_special() => break,
989 '/' | '?' | '#' => break,
990 '\t' | '\n' | '\r' => {
991 has_ignored_chars = true;
992 }
993 '[' => {
994 inside_square_brackets = true;
995 non_ignored_chars += 1
996 }
997 ']' => {
998 inside_square_brackets = false;
999 non_ignored_chars += 1
1000 }
1001 _ => non_ignored_chars += 1,
1002 }
1003 bytes += c.len_utf8();
1004 }
1005 let replaced: String;
1006 let host_str;
1007 {
1008 let host_input = input.by_ref().take(non_ignored_chars);
1009 if has_ignored_chars {
1010 replaced = host_input.collect();
1011 host_str = &*replaced
1012 } else {
1013 for _ in host_input {}
1014 host_str = &input_str[..bytes]
1015 }
1016 }
1017 if scheme_type == SchemeType::SpecialNotFile && host_str.is_empty() {
1018 return Err(ParseError::EmptyHost);
1019 }
1020 if !scheme_type.is_special() {
1021 let host = Host::parse_opaque(host_str)?;
1022 return Ok((host, input));
1023 }
1024 let host = Host::parse(host_str)?;
1025 Ok((host, input))
1026 }
1027
1028 fn get_file_host(input: Input<'_>) -> ParseResult<(Host<String>, Input<'_>)> {
1029 let (_, host_str, remaining) = Parser::file_host(input)?;
1030 let host = match Host::parse(&host_str)? {
1031 Host::Domain(ref d) if d == "localhost" => Host::Domain("".to_string()),
1032 host => host,
1033 };
1034 Ok((host, remaining))
1035 }
1036
1037 fn parse_file_host<'i>(
1038 &mut self,
1039 input: Input<'i>,
1040 ) -> ParseResult<(bool, HostInternal, Input<'i>)> {
1041 let has_host;
1042 let (_, host_str, remaining) = Parser::file_host(input)?;
1043 let host = if host_str.is_empty() {
1044 has_host = false;
1045 HostInternal::None
1046 } else {
1047 match Host::parse(&host_str)? {
1048 Host::Domain(ref d) if d == "localhost" => {
1049 has_host = false;
1050 HostInternal::None
1051 }
1052 host => {
1053 write!(&mut self.serialization, "{}", host).unwrap();
1054 has_host = true;
1055 host.into()
1056 }
1057 }
1058 };
1059 Ok((has_host, host, remaining))
1060 }
1061
1062 pub fn file_host(input: Input) -> ParseResult<(bool, String, Input)> {
1063 // Undo the Input abstraction here to avoid allocating in the common case
1064 // where the host part of the input does not contain any tab or newline
1065 let input_str = input.chars.as_str();
1066 let mut has_ignored_chars = false;
1067 let mut non_ignored_chars = 0;
1068 let mut bytes = 0;
1069 for c in input_str.chars() {
1070 match c {
1071 '/' | '\\' | '?' | '#' => break,
1072 '\t' | '\n' | '\r' => has_ignored_chars = true,
1073 _ => non_ignored_chars += 1,
1074 }
1075 bytes += c.len_utf8();
1076 }
1077 let replaced: String;
1078 let host_str;
1079 let mut remaining = input.clone();
1080 {
1081 let host_input = remaining.by_ref().take(non_ignored_chars);
1082 if has_ignored_chars {
1083 replaced = host_input.collect();
1084 host_str = &*replaced
1085 } else {
1086 for _ in host_input {}
1087 host_str = &input_str[..bytes]
1088 }
1089 }
1090 if is_windows_drive_letter(host_str) {
1091 return Ok((false, "".to_string(), input));
1092 }
1093 Ok((true, host_str.to_string(), remaining))
1094 }
1095
1096 pub fn parse_port<P>(
1097 mut input: Input<'_>,
1098 default_port: P,
1099 context: Context,
1100 ) -> ParseResult<(Option<u16>, Input<'_>)>
1101 where
1102 P: Fn() -> Option<u16>,
1103 {
1104 let mut port: u32 = 0;
1105 let mut has_any_digit = false;
1106 while let (Some(c), remaining) = input.split_first() {
1107 if let Some(digit) = c.to_digit(10) {
1108 port = port * 10 + digit;
1109 if port > ::std::u16::MAX as u32 {
1110 return Err(ParseError::InvalidPort);
1111 }
1112 has_any_digit = true;
1113 } else if context == Context::UrlParser && !matches!(c, '/' | '\\' | '?' | '#') {
1114 return Err(ParseError::InvalidPort);
1115 } else {
1116 break;
1117 }
1118 input = remaining;
1119 }
1120 let mut opt_port = Some(port as u16);
1121 if !has_any_digit || opt_port == default_port() {
1122 opt_port = None;
1123 }
1124 Ok((opt_port, input))
1125 }
1126
1127 pub fn parse_path_start<'i>(
1128 &mut self,
1129 scheme_type: SchemeType,
1130 has_host: &mut bool,
1131 input: Input<'i>,
1132 ) -> Input<'i> {
1133 let path_start = self.serialization.len();
1134 let (maybe_c, remaining) = input.split_first();
1135 // If url is special, then:
1136 if scheme_type.is_special() {
1137 if maybe_c == Some('\\') {
1138 // If c is U+005C (\), validation error.
1139 self.log_violation(SyntaxViolation::Backslash);
1140 }
1141 // A special URL always has a non-empty path.
1142 if !self.serialization.ends_with('/') {
1143 self.serialization.push('/');
1144 // We have already made sure the forward slash is present.
1145 if maybe_c == Some('/') || maybe_c == Some('\\') {
1146 return self.parse_path(scheme_type, has_host, path_start, remaining);
1147 }
1148 }
1149 return self.parse_path(scheme_type, has_host, path_start, input);
1150 } else if maybe_c == Some('?') || maybe_c == Some('#') {
1151 // Otherwise, if state override is not given and c is U+003F (?),
1152 // set url’s query to the empty string and state to query state.
1153 // Otherwise, if state override is not given and c is U+0023 (#),
1154 // set url’s fragment to the empty string and state to fragment state.
1155 // The query and path states will be handled by the caller.
1156 return input;
1157 }
1158
1159 if maybe_c.is_some() && maybe_c != Some('/') {
1160 self.serialization.push('/');
1161 }
1162 // Otherwise, if c is not the EOF code point:
1163 self.parse_path(scheme_type, has_host, path_start, input)
1164 }
1165
1166 pub fn parse_path<'i>(
1167 &mut self,
1168 scheme_type: SchemeType,
1169 has_host: &mut bool,
1170 path_start: usize,
1171 mut input: Input<'i>,
1172 ) -> Input<'i> {
1173 // Relative path state
1174 loop {
1175 let segment_start = self.serialization.len();
1176 let mut ends_with_slash = false;
1177 loop {
1178 let input_before_c = input.clone();
1179 let (c, utf8_c) = if let Some(x) = input.next_utf8() {
1180 x
1181 } else {
1182 break;
1183 };
1184 match c {
1185 '/' if self.context != Context::PathSegmentSetter => {
1186 self.serialization.push(c);
1187 ends_with_slash = true;
1188 break;
1189 }
1190 '\\' if self.context != Context::PathSegmentSetter
1191 && scheme_type.is_special() =>
1192 {
1193 self.log_violation(SyntaxViolation::Backslash);
1194 self.serialization.push('/');
1195 ends_with_slash = true;
1196 break;
1197 }
1198 '?' | '#' if self.context == Context::UrlParser => {
1199 input = input_before_c;
1200 break;
1201 }
1202 _ => {
1203 self.check_url_code_point(c, &input);
1204 if self.context == Context::PathSegmentSetter {
1205 if scheme_type.is_special() {
1206 self.serialization
1207 .extend(utf8_percent_encode(utf8_c, SPECIAL_PATH_SEGMENT));
1208 } else {
1209 self.serialization
1210 .extend(utf8_percent_encode(utf8_c, PATH_SEGMENT));
1211 }
1212 } else {
1213 self.serialization.extend(utf8_percent_encode(utf8_c, PATH));
1214 }
1215 }
1216 }
1217 }
1218 let segment_before_slash = if ends_with_slash {
1219 &self.serialization[segment_start..self.serialization.len() - 1]
1220 } else {
1221 &self.serialization[segment_start..self.serialization.len()]
1222 };
1223 match segment_before_slash {
1224 // If buffer is a double-dot path segment, shorten url’s path,
1225 ".." | "%2e%2e" | "%2e%2E" | "%2E%2e" | "%2E%2E" | "%2e." | "%2E." | ".%2e"
1226 | ".%2E" => {
1227 debug_assert!(self.serialization.as_bytes()[segment_start - 1] == b'/');
1228 self.serialization.truncate(segment_start);
1229 if self.serialization.ends_with('/')
1230 && Parser::last_slash_can_be_removed(&self.serialization, path_start)
1231 {
1232 self.serialization.pop();
1233 }
1234 self.shorten_path(scheme_type, path_start);
1235
1236 // and then if neither c is U+002F (/), nor url is special and c is U+005C (\), append the empty string to url’s path.
1237 if ends_with_slash && !self.serialization.ends_with('/') {
1238 self.serialization.push('/');
1239 }
1240 }
1241 // Otherwise, if buffer is a single-dot path segment and if neither c is U+002F (/),
1242 // nor url is special and c is U+005C (\), append the empty string to url’s path.
1243 "." | "%2e" | "%2E" => {
1244 self.serialization.truncate(segment_start);
1245 if !self.serialization.ends_with('/') {
1246 self.serialization.push('/');
1247 }
1248 }
1249 _ => {
1250 // If url’s scheme is "file", url’s path is empty, and buffer is a Windows drive letter, then
1251 if scheme_type.is_file() && is_windows_drive_letter(segment_before_slash) {
1252 // Replace the second code point in buffer with U+003A (:).
1253 if let Some(c) = segment_before_slash.chars().next() {
1254 self.serialization.truncate(segment_start);
1255 self.serialization.push(c);
1256 self.serialization.push(':');
1257 if ends_with_slash {
1258 self.serialization.push('/');
1259 }
1260 }
1261 // If url’s host is neither the empty string nor null,
1262 // validation error, set url’s host to the empty string.
1263 if *has_host {
1264 self.log_violation(SyntaxViolation::FileWithHostAndWindowsDrive);
1265 *has_host = false; // FIXME account for this in callers
1266 }
1267 }
1268 }
1269 }
1270 if !ends_with_slash {
1271 break;
1272 }
1273 }
1274 if scheme_type.is_file() {
1275 // while url’s path’s size is greater than 1
1276 // and url’s path[0] is the empty string,
1277 // validation error, remove the first item from url’s path.
1278 //FIXME: log violation
1279 let path = self.serialization.split_off(path_start);
1280 self.serialization.push('/');
1281 self.serialization.push_str(path.trim_start_matches('/'));
1282 }
1283
1284 input
1285 }
nl -ba url/src/parser.rs | sed -n '1285,1495p'
Chunk ID: 479405
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2225
Output:
1285 }
1286
1287 fn last_slash_can_be_removed(serialization: &str, path_start: usize) -> bool {
1288 let url_before_segment = &serialization[..serialization.len() - 1];
1289 if let Some(segment_before_start) = url_before_segment.rfind('/') {
1290 // Do not remove the root slash
1291 segment_before_start >= path_start
1292 // Or a windows drive letter slash
1293 && !path_starts_with_windows_drive_letter(&serialization[segment_before_start..])
1294 } else {
1295 false
1296 }
1297 }
1298
1299 /// https://url.spec.whatwg.org/#shorten-a-urls-path
1300 fn shorten_path(&mut self, scheme_type: SchemeType, path_start: usize) {
1301 // If path is empty, then return.
1302 if self.serialization.len() == path_start {
1303 return;
1304 }
1305 // If url’s scheme is "file", path’s size is 1, and path[0] is a normalized Windows drive letter, then return.
1306 if scheme_type.is_file()
1307 && is_normalized_windows_drive_letter(&self.serialization[path_start..])
1308 {
1309 return;
1310 }
1311 // Remove path’s last item.
1312 self.pop_path(scheme_type, path_start);
1313 }
1314
1315 /// https://url.spec.whatwg.org/#pop-a-urls-path
1316 fn pop_path(&mut self, scheme_type: SchemeType, path_start: usize) {
1317 if self.serialization.len() > path_start {
1318 let slash_position = self.serialization[path_start..].rfind('/').unwrap();
1319 // + 1 since rfind returns the position before the slash.
1320 let segment_start = path_start + slash_position + 1;
1321 // Don’t pop a Windows drive letter
1322 if !(scheme_type.is_file()
1323 && is_normalized_windows_drive_letter(&self.serialization[segment_start..]))
1324 {
1325 self.serialization.truncate(segment_start);
1326 }
1327 }
1328 }
1329
1330 pub fn parse_cannot_be_a_base_path<'i>(&mut self, mut input: Input<'i>) -> Input<'i> {
1331 loop {
1332 let input_before_c = input.clone();
1333 match input.next_utf8() {
1334 Some(('?', _)) | Some(('#', _)) if self.context == Context::UrlParser => {
1335 return input_before_c
1336 }
1337 Some((c, utf8_c)) => {
1338 self.check_url_code_point(c, &input);
1339 self.serialization
1340 .extend(utf8_percent_encode(utf8_c, CONTROLS));
1341 }
1342 None => return input,
1343 }
1344 }
1345 }
1346
1347 #[allow(clippy::too_many_arguments)]
1348 fn with_query_and_fragment(
1349 mut self,
1350 scheme_type: SchemeType,
1351 scheme_end: u32,
1352 username_end: u32,
1353 host_start: u32,
1354 host_end: u32,
1355 host: HostInternal,
1356 port: Option<u16>,
1357 path_start: u32,
1358 remaining: Input<'_>,
1359 ) -> ParseResult<Url> {
1360 let (query_start, fragment_start) =
1361 self.parse_query_and_fragment(scheme_type, scheme_end, remaining)?;
1362 Ok(Url {
1363 serialization: self.serialization,
1364 scheme_end,
1365 username_end,
1366 host_start,
1367 host_end,
1368 host,
1369 port,
1370 path_start,
1371 query_start,
1372 fragment_start,
1373 })
1374 }
1375
1376 /// Return (query_start, fragment_start)
1377 fn parse_query_and_fragment(
1378 &mut self,
1379 scheme_type: SchemeType,
1380 scheme_end: u32,
1381 mut input: Input<'_>,
1382 ) -> ParseResult<(Option<u32>, Option<u32>)> {
1383 let mut query_start = None;
1384 match input.next() {
1385 Some('#') => {}
1386 Some('?') => {
1387 query_start = Some(to_u32(self.serialization.len())?);
1388 self.serialization.push('?');
1389 let remaining = self.parse_query(scheme_type, scheme_end, input);
1390 if let Some(remaining) = remaining {
1391 input = remaining
1392 } else {
1393 return Ok((query_start, None));
1394 }
1395 }
1396 None => return Ok((None, None)),
1397 _ => panic!("Programming error. parse_query_and_fragment() called without ? or #"),
1398 }
1399
1400 let fragment_start = to_u32(self.serialization.len())?;
1401 self.serialization.push('#');
1402 self.parse_fragment(input);
1403 Ok((query_start, Some(fragment_start)))
1404 }
1405
1406 pub fn parse_query<'i>(
1407 &mut self,
1408 scheme_type: SchemeType,
1409 scheme_end: u32,
1410 mut input: Input<'i>,
1411 ) -> Option<Input<'i>> {
1412 let len = input.chars.as_str().len();
1413 let mut query = String::with_capacity(len); // FIXME: use a streaming decoder instead
1414 let mut remaining = None;
1415 while let Some(c) = input.next() {
1416 if c == '#' && self.context == Context::UrlParser {
1417 remaining = Some(input);
1418 break;
1419 } else {
1420 self.check_url_code_point(c, &input);
1421 query.push(c);
1422 }
1423 }
1424
1425 let encoding = match &self.serialization[..scheme_end as usize] {
1426 "http" | "https" | "file" | "ftp" => self.query_encoding_override,
1427 _ => None,
1428 };
1429 let query_bytes = if let Some(o) = encoding {
1430 o(&query)
1431 } else {
1432 query.as_bytes().into()
1433 };
1434 let set = if scheme_type.is_special() {
1435 SPECIAL_QUERY
1436 } else {
1437 QUERY
1438 };
1439 self.serialization.extend(percent_encode(&query_bytes, set));
1440 remaining
1441 }
1442
1443 fn fragment_only(mut self, base_url: &Url, mut input: Input<'_>) -> ParseResult<Url> {
1444 let before_fragment = match base_url.fragment_start {
1445 Some(i) => base_url.slice(..i),
1446 None => &*base_url.serialization,
1447 };
1448 debug_assert!(self.serialization.is_empty());
1449 self.serialization
1450 .reserve(before_fragment.len() + input.chars.as_str().len());
1451 self.serialization.push_str(before_fragment);
1452 self.serialization.push('#');
1453 let next = input.next();
1454 debug_assert!(next == Some('#'));
1455 self.parse_fragment(input);
1456 Ok(Url {
1457 serialization: self.serialization,
1458 fragment_start: Some(to_u32(before_fragment.len())?),
1459 ..*base_url
1460 })
1461 }
1462
1463 pub fn parse_fragment(&mut self, mut input: Input<'_>) {
1464 while let Some((c, utf8_c)) = input.next_utf8() {
1465 if c == '\0' {
1466 self.log_violation(SyntaxViolation::NullInFragment)
1467 } else {
1468 self.check_url_code_point(c, &input);
1469 }
1470 self.serialization
1471 .extend(utf8_percent_encode(utf8_c, FRAGMENT));
1472 }
1473 }
1474
1475 fn check_url_code_point(&self, c: char, input: &Input<'_>) {
1476 if let Some(vfn) = self.violation_fn {
1477 if c == '%' {
1478 let mut input = input.clone();
1479 if !matches!((input.next(), input.next()), (Some(a), Some(b))
1480 if is_ascii_hex_digit(a) && is_ascii_hex_digit(b))
1481 {
1482 vfn(SyntaxViolation::PercentDecode)
1483 }
1484 } else if !is_url_code_point(c) {
1485 vfn(SyntaxViolation::NonUrlCodePoint)
1486 }
1487 }
1488 }
1489 }
1490
1491 #[inline]
1492 fn is_ascii_hex_digit(c: char) -> bool {
1493 matches!(c, 'a'..='f' | 'A'..='F' | '0'..='9')
1494 }
1495
rg "struct Url|path_start|serialization" url/src/lib.rs -n | head -80
Chunk ID: 4be5df
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1266
Output:
170:pub struct Url {
180: serialization: String,
189: path_start: u32, // Before initial '/', if any
246: serialization: String::with_capacity(input.len()),
340: .serialization
346: let start = self.serialization.len() - trailing_space_count;
348: self.serialization.truncate(start);
549: /// Return the serialization of this URL.
551: /// This is fast since that serialization is already stored in the `Url` struct.
569: &self.serialization
572: /// Return the serialization of this URL.
609: self.serialization
623: self.serialization))
639: if self.username_end != self.serialization.len() as u32 {
665: if self.path_start == self.host_end {
669: let port_str = self.slice(self.host_end + 1..self.path_start);
676: self.path_start as usize == self.serialization.len()
677: || matches!(self.byte_at(self.path_start), b'/' | b'#' | b'?')
686: assert_eq!(self.path_start, self.scheme_end + 1);
689: assert!(start >= self.path_start);
693: assert!(start >= self.path_start);
701: assert_eq!(&self.serialization, &other.serialization);
713: assert_eq!(self.path_start, other.path_start);
882: if self.has_authority() && self.path_start > self.scheme_end + scheme_separator_len {
883: self.slice(self.scheme_end + scheme_separator_len..self.path_start)
978: && self.username_end != self.serialization.len() as u32
1126: /// Note that default port numbers are never reflected by the serialization,
1265: (None, None) => self.slice(self.path_start..),
1267: self.slice(self.path_start..next_component_start)
1422: let mut parser = Parser::for_setter(mem::take(&mut self.serialization));
1424: self.serialization = parser.serialization;
1455: self.serialization.truncate(start as usize);
1459: self.fragment_start = Some(to_u32(self.serialization.len()).unwrap());
1460: self.serialization.push('#');
1472: self.serialization.truncate(start as usize);
1480: self.fragment_start = Some(to_u32(self.serialization.len()).unwrap());
1481: self.serialization.push('#');
1482: self.serialization.push_str(fragment);
1511: self.serialization.truncate(start as usize);
1515: self.query_start = Some(to_u32(self.serialization.len()).unwrap());
1516: self.serialization.push('?');
1575: query_start = self.serialization.len();
1577: self.serialization.push('?');
1591: self.serialization.truncate(i as usize);
1635: let old_after_path_pos = to_u32(self.serialization.len()).unwrap();
1638: self.serialization.truncate(self.path_start as usize);
1642: parser.serialization.push_str("%2F");
1648: parser.parse_path_start(scheme_type, &mut has_host, parser::Input::new(path));
1667: let new_after_path_position = to_u32(self.serialization.len()).unwrap();
1678: self.serialization.push_str(after_path)
1683: /// Note that default port numbers are not reflected in the serialization.
1758: self.serialization
1759: .drain(self.host_end as usize..self.path_start as usize);
1760: let offset = self.path_start - self.host_end;
1761: self.path_start = self.host_end;
1771: let path_and_after = self.slice(self.path_start..).to_owned();
1772: self.serialization.truncate(self.host_end as usize);
1773: write!(&mut self.serialization, ":{}", new).unwrap();
1774: let old_path_start = self.path_start;
1775: let new_path_start = to_u32(self.serialization.len()).unwrap();
1776: self.path_start = new_path_start;
1778: *index -= old_path_start;
1779: *index += new_path_start;
1787: self.serialization.push_str(&path_and_after);
1909: } else if self.serialization.len() == self.path_start as usize {
1910: self.serialization.push('/');
1913: debug_assert!(self.byte_at(self.path_start) == b'/');
1915: let new_path_start = if scheme_type.is_file() {
1921: self.serialization
1922: .drain(new_path_start as usize..self.path_start as usize);
1923: let offset = self.path_start - new_path_start;
1924: self.path_start = new_path_start;
1925: self.username_end = new_path_start;
1926: self.host_start = new_path_start;
1927: self.host_end = new_path_start;
1942: self.path_start
1947: self.serialization.truncate(self.host_start as usize);
1951: self.serialization.push('/');
1952: self.serialization.push('/');
1956: write!(&mut self.serialization, "{}", host).unwrap();
nl -ba url/src/lib.rs | sed -n '200,320p'
Chunk ID: 19b7c3
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1170
Output:
200 }
201
202 impl<'a> ParseOptions<'a> {
203 /// Change the base URL
204 pub fn base_url(mut self, new: Option<&'a Url>) -> Self {
205 self.base_url = new;
206 self
207 }
208
209 /// Override the character encoding of query strings.
210 /// This is a legacy concept only relevant for HTML.
211 pub fn encoding_override(mut self, new: EncodingOverride<'a>) -> Self {
212 self.encoding_override = new;
213 self
214 }
215
216 /// Call the provided function or closure for a non-fatal `SyntaxViolation`
217 /// when it occurs during parsing. Note that since the provided function is
218 /// `Fn`, the caller might need to utilize _interior mutability_, such as with
219 /// a `RefCell`, to collect the violations.
220 ///
221 /// ## Example
222 /// ```
223 /// use std::cell::RefCell;
224 /// use url::{Url, SyntaxViolation};
225 /// # use url::ParseError;
226 /// # fn run() -> Result<(), url::ParseError> {
227 /// let violations = RefCell::new(Vec::new());
228 /// let url = Url::options()
229 /// .syntax_violation_callback(Some(&|v| violations.borrow_mut().push(v)))
230 /// .parse("https:////example.com")?;
231 /// assert_eq!(url.as_str(), "https://example.com/");
232 /// assert_eq!(violations.into_inner(),
233 /// vec!(SyntaxViolation::ExpectedDoubleSlash));
234 /// # Ok(())
235 /// # }
236 /// # run().unwrap();
237 /// ```
238 pub fn syntax_violation_callback(mut self, new: Option<&'a dyn Fn(SyntaxViolation)>) -> Self {
239 self.violation_fn = new;
240 self
241 }
242
243 /// Parse an URL string with the configuration so far.
244 pub fn parse(self, input: &str) -> Result<Url, crate::ParseError> {
245 Parser {
246 serialization: String::with_capacity(input.len()),
247 base_url: self.base_url,
248 query_encoding_override: self.encoding_override,
249 violation_fn: self.violation_fn,
250 context: Context::UrlParser,
251 }
252 .parse_url(input)
253 }
254 }
255
256 impl Url {
257 /// Parse an absolute URL from a string.
258 ///
259 /// # Examples
260 ///
261 /// ```rust
262 /// use url::Url;
263 /// # use url::ParseError;
264 ///
265 /// # fn run() -> Result<(), ParseError> {
266 /// let url = Url::parse("https://example.net")?;
267 /// # Ok(())
268 /// # }
269 /// # run().unwrap();
270 /// ```
271 ///
272 /// # Errors
273 ///
274 /// If the function can not parse an absolute URL from the given string,
275 /// a [`ParseError`] variant will be returned.
276 ///
277 /// [`ParseError`]: enum.ParseError.html
278 #[inline]
279 pub fn parse(input: &str) -> Result<Url, crate::ParseError> {
280 Url::options().parse(input)
281 }
282
283 /// Parse an absolute URL from a string and add params to its query string.
284 ///
285 /// Existing params are not removed.
286 ///
287 /// # Examples
288 ///
289 /// ```rust
290 /// use url::Url;
291 /// # use url::ParseError;
292 ///
293 /// # fn run() -> Result<(), ParseError> {
294 /// let url = Url::parse_with_params("https://example.net?dont=clobberme",
295 /// &[("lang", "rust"), ("browser", "servo")])?;
296 /// assert_eq!("https://example.net/?dont=clobberme&lang=rust&browser=servo", url.as_str());
297 /// # Ok(())
298 /// # }
299 /// # run().unwrap();
300 /// ```
301 ///
302 /// # Errors
303 ///
304 /// If the function can not parse an absolute URL from the given string,
305 /// a [`ParseError`] variant will be returned.
306 ///
307 /// [`ParseError`]: enum.ParseError.html
308 #[inline]
309 pub fn parse_with_params<I, K, V>(input: &str, iter: I) -> Result<Url, crate::ParseError>
310 where
311 I: IntoIterator,
312 I::Item: Borrow<(K, V)>,
313 K: AsRef<str>,
314 V: AsRef<str>,
315 {
316 let mut url = Url::options().parse(input);
317
318 if let Ok(ref mut url) = url {
319 url.query_pairs_mut().extend_pairs(iter);
320 }
rg "with_query_and_fragment|parse_query_and_fragment|pop_path|parse_path\(" url/src/parser.rs -nChunk ID: 016a2b
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 574
Output:
479: self.parse_path(scheme_type, &mut false, path_start, input)
483: self.with_query_and_fragment(
525: self.parse_path(SchemeType::File, &mut has_host, path_start, remaining)
537: self.parse_query_and_fragment(scheme_type, scheme_end, remaining)?;
581: self.parse_path(SchemeType::File, &mut false, host_end, parse_path_input);
586: self.parse_query_and_fragment(scheme_type, scheme_end, remaining)?;
626: self.parse_query_and_fragment(scheme_type, base_url.scheme_end, input)?;
643: let remaining = self.parse_path(
649: self.with_query_and_fragment(
665: self.parse_path(SchemeType::File, &mut false, path_start, input);
667: self.parse_query_and_fragment(SchemeType::File, scheme_end, remaining)?;
688: let remaining = self.parse_path(SchemeType::File, &mut false, path_start, input);
690: self.parse_query_and_fragment(SchemeType::File, scheme_end, remaining)?;
738: self.parse_query_and_fragment(scheme_type, base_url.scheme_end, input)?;
769: let remaining = self.parse_path(
775: self.with_query_and_fragment(
794: self.pop_path(scheme_type, base_url.path_start as usize);
803: (Some('/'), remaining) => self.parse_path(
810: self.parse_path(scheme_type, &mut true, base_url.path_start as usize, input)
813: self.with_query_and_fragment(
850: self.with_query_and_fragment(
1146: return self.parse_path(scheme_type, has_host, path_start, remaining);
1149: return self.parse_path(scheme_type, has_host, path_start, input);
1163: self.parse_path(scheme_type, has_host, path_start, input)
1312: self.pop_path(scheme_type, path_start);
1316: fn pop_path(&mut self, scheme_type: SchemeType, path_start: usize) {
1348: fn with_query_and_fragment(
1361: self.parse_query_and_fragment(scheme_type, scheme_end, remaining)?;
1377: fn parse_query_and_fragment(
1397: _ => panic!("Programming error. parse_query_and_fragment() called without ? or #"),
nl -ba url/src/lib.rs | sed -n '150,205p'
Chunk ID: 96748d
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 535
Output:
150 use std::convert::TryFrom;
151
152 pub use crate::host::Host;
153 pub use crate::origin::{OpaqueOrigin, Origin};
154 pub use crate::parser::{ParseError, SyntaxViolation};
155 pub use crate::path_segments::PathSegmentsMut;
156 pub use crate::slicing::Position;
157 pub use form_urlencoded::EncodingOverride;
158
159 mod host;
160 mod origin;
161 mod parser;
162 mod path_segments;
163 mod slicing;
164
165 #[doc(hidden)]
166 pub mod quirks;
167
168 /// A parsed URL record.
169 #[derive(Clone)]
170 pub struct Url {
171 /// Syntax in pseudo-BNF:
172 ///
173 /// url = scheme ":" [ hierarchical | non-hierarchical ] [ "?" query ]? [ "#" fragment ]?
174 /// non-hierarchical = non-hierarchical-path
175 /// non-hierarchical-path = /* Does not start with "/" */
176 /// hierarchical = authority? hierarchical-path
177 /// authority = "//" userinfo? host [ ":" port ]?
178 /// userinfo = username [ ":" password ]? "@"
179 /// hierarchical-path = [ "/" path-segment ]+
180 serialization: String,
181
182 // Components
183 scheme_end: u32, // Before ':'
184 username_end: u32, // Before ':' (if a password is given) or '@' (if not)
185 host_start: u32,
186 host_end: u32,
187 host: HostInternal,
188 port: Option<u16>,
189 path_start: u32, // Before initial '/', if any
190 query_start: Option<u32>, // Before '?', unlike Position::QueryStart
191 fragment_start: Option<u32>, // Before '#', unlike Position::FragmentStart
192 }
193
194 /// Full configuration for the URL parser.
195 #[derive(Copy, Clone)]
196 pub struct ParseOptions<'a> {
197 base_url: Option<&'a Url>,
198 encoding_override: EncodingOverride<'a>,
199 violation_fn: Option<&'a dyn Fn(SyntaxViolation)>,
200 }
201
202 impl<'a> ParseOptions<'a> {
203 /// Change the base URL
204 pub fn base_url(mut self, new: Option<&'a Url>) -> Self {
205 self.base_url = new;
nl -ba url/src/lib.rs | sed -n '840,910p'
Chunk ID: 2f81d1
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 787
Output:
840 /// # run().unwrap();
841 /// ```
842 #[inline]
843 pub fn has_authority(&self) -> bool {
844 debug_assert!(self.byte_at(self.scheme_end) == b':');
845 self.slice(self.scheme_end..).starts_with("://")
846 }
847
848 /// Return the authority of this URL as an ASCII string.
849 ///
850 /// Non-ASCII domains are punycode-encoded per IDNA if this is the host
851 /// of a special URL, or percent encoded for non-special URLs.
852 /// IPv6 addresses are given between `[` and `]` brackets.
853 /// Ports are omitted if they match the well known port of a special URL.
854 ///
855 /// Username and password are percent-encoded.
856 ///
857 /// See also the `has_authority` method.
858 ///
859 /// # Examples
860 ///
861 /// ```
862 /// use url::Url;
863 /// # use url::ParseError;
864 ///
865 /// # fn run() -> Result<(), ParseError> {
866 /// let url = Url::parse("unix:/run/foo.socket")?;
867 /// assert_eq!(url.authority(), "");
868 /// let url = Url::parse("file:///tmp/foo")?;
869 /// assert_eq!(url.authority(), "");
870 /// let url = Url::parse("https://user:password@example.com/tmp/foo")?;
871 /// assert_eq!(url.authority(), "user:password@example.com");
872 /// let url = Url::parse("irc://àlex.рф.example.com:6667/foo")?;
873 /// assert_eq!(url.authority(), "%C3%A0lex.%D1%80%D1%84.example.com:6667");
874 /// let url = Url::parse("http://àlex.рф.example.com:80/foo")?;
875 /// assert_eq!(url.authority(), "xn--lex-8ka.xn--p1ai.example.com");
876 /// # Ok(())
877 /// # }
878 /// # run().unwrap();
879 /// ```
880 pub fn authority(&self) -> &str {
881 let scheme_separator_len = "://".len() as u32;
882 if self.has_authority() && self.path_start > self.scheme_end + scheme_separator_len {
883 self.slice(self.scheme_end + scheme_separator_len..self.path_start)
884 } else {
885 ""
886 }
887 }
888
889 /// Return whether this URL is a cannot-be-a-base URL,
890 /// meaning that parsing a relative URL string with this URL as the base will return an error.
891 ///
892 /// This is the case if the scheme and `:` delimiter are not followed by a `/` slash,
893 /// as is typically the case of `data:` and `mailto:` URLs.
894 ///
895 /// # Examples
896 ///
897 /// ```
898 /// use url::Url;
899 /// # use url::ParseError;
900 ///
901 /// # fn run() -> Result<(), ParseError> {
902 /// let url = Url::parse("ftp://rms@example.com")?;
903 /// assert!(!url.cannot_be_a_base());
904 ///
905 /// let url = Url::parse("unix:/run/foo.socket")?;
906 /// assert!(!url.cannot_be_a_base());
907 ///
908 /// let url = Url::parse("data:text/plain,Stuff")?;
909 /// assert!(url.cannot_be_a_base());
910 /// # Ok(())
nl -ba url/src/lib.rs | sed -n '1400,1695p'
Chunk ID: d4f635
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 3217
Output:
1400 /// # use url::ParseError;
1401 ///
1402 /// # fn run() -> Result<(), ParseError> {
1403 /// let url = Url::parse("https://example.com/data.csv#row=4")?;
1404 ///
1405 /// assert_eq!(url.fragment(), Some("row=4"));
1406 ///
1407 /// let url = Url::parse("https://example.com/data.csv#cell=4,1-6,2")?;
1408 ///
1409 /// assert_eq!(url.fragment(), Some("cell=4,1-6,2"));
1410 /// # Ok(())
1411 /// # }
1412 /// # run().unwrap();
1413 /// ```
1414 pub fn fragment(&self) -> Option<&str> {
1415 self.fragment_start.map(|start| {
1416 debug_assert!(self.byte_at(start) == b'#');
1417 self.slice(start + 1..)
1418 })
1419 }
1420
1421 fn mutate<F: FnOnce(&mut Parser<'_>) -> R, R>(&mut self, f: F) -> R {
1422 let mut parser = Parser::for_setter(mem::take(&mut self.serialization));
1423 let result = f(&mut parser);
1424 self.serialization = parser.serialization;
1425 result
1426 }
1427
1428 /// Change this URL’s fragment identifier.
1429 ///
1430 /// # Examples
1431 ///
1432 /// ```rust
1433 /// use url::Url;
1434 /// # use url::ParseError;
1435 ///
1436 /// # fn run() -> Result<(), ParseError> {
1437 /// let mut url = Url::parse("https://example.com/data.csv")?;
1438 /// assert_eq!(url.as_str(), "https://example.com/data.csv");
1439
1440 /// url.set_fragment(Some("cell=4,1-6,2"));
1441 /// assert_eq!(url.as_str(), "https://example.com/data.csv#cell=4,1-6,2");
1442 /// assert_eq!(url.fragment(), Some("cell=4,1-6,2"));
1443 ///
1444 /// url.set_fragment(None);
1445 /// assert_eq!(url.as_str(), "https://example.com/data.csv");
1446 /// assert!(url.fragment().is_none());
1447 /// # Ok(())
1448 /// # }
1449 /// # run().unwrap();
1450 /// ```
1451 pub fn set_fragment(&mut self, fragment: Option<&str>) {
1452 // Remove any previous fragment
1453 if let Some(start) = self.fragment_start {
1454 debug_assert!(self.byte_at(start) == b'#');
1455 self.serialization.truncate(start as usize);
1456 }
1457 // Write the new one
1458 if let Some(input) = fragment {
1459 self.fragment_start = Some(to_u32(self.serialization.len()).unwrap());
1460 self.serialization.push('#');
1461 self.mutate(|parser| parser.parse_fragment(parser::Input::no_trim(input)))
1462 } else {
1463 self.fragment_start = None;
1464 self.strip_trailing_spaces_from_opaque_path();
1465 }
1466 }
1467
1468 fn take_fragment(&mut self) -> Option<String> {
1469 self.fragment_start.take().map(|start| {
1470 debug_assert!(self.byte_at(start) == b'#');
1471 let fragment = self.slice(start + 1..).to_owned();
1472 self.serialization.truncate(start as usize);
1473 fragment
1474 })
1475 }
1476
1477 fn restore_already_parsed_fragment(&mut self, fragment: Option<String>) {
1478 if let Some(ref fragment) = fragment {
1479 assert!(self.fragment_start.is_none());
1480 self.fragment_start = Some(to_u32(self.serialization.len()).unwrap());
1481 self.serialization.push('#');
1482 self.serialization.push_str(fragment);
1483 }
1484 }
1485
1486 /// Change this URL’s query string.
1487 ///
1488 /// # Examples
1489 ///
1490 /// ```rust
1491 /// use url::Url;
1492 /// # use url::ParseError;
1493 ///
1494 /// # fn run() -> Result<(), ParseError> {
1495 /// let mut url = Url::parse("https://example.com/products")?;
1496 /// assert_eq!(url.as_str(), "https://example.com/products");
1497 ///
1498 /// url.set_query(Some("page=2"));
1499 /// assert_eq!(url.as_str(), "https://example.com/products?page=2");
1500 /// assert_eq!(url.query(), Some("page=2"));
1501 /// # Ok(())
1502 /// # }
1503 /// # run().unwrap();
1504 /// ```
1505 pub fn set_query(&mut self, query: Option<&str>) {
1506 let fragment = self.take_fragment();
1507
1508 // Remove any previous query
1509 if let Some(start) = self.query_start.take() {
1510 debug_assert!(self.byte_at(start) == b'?');
1511 self.serialization.truncate(start as usize);
1512 }
1513 // Write the new query, if any
1514 if let Some(input) = query {
1515 self.query_start = Some(to_u32(self.serialization.len()).unwrap());
1516 self.serialization.push('?');
1517 let scheme_type = SchemeType::from(self.scheme());
1518 let scheme_end = self.scheme_end;
1519 self.mutate(|parser| {
1520 let vfn = parser.violation_fn;
1521 parser.parse_query(
1522 scheme_type,
1523 scheme_end,
1524 parser::Input::trim_tab_and_newlines(input, vfn),
1525 )
1526 });
1527 } else {
1528 self.query_start = None;
1529 self.strip_trailing_spaces_from_opaque_path();
1530 }
1531
1532 self.restore_already_parsed_fragment(fragment);
1533 }
1534
1535 /// Manipulate this URL’s query string, viewed as a sequence of name/value pairs
1536 /// in `application/x-www-form-urlencoded` syntax.
1537 ///
1538 /// The return value has a method-chaining API:
1539 ///
1540 /// ```rust
1541 /// # use url::{Url, ParseError};
1542 ///
1543 /// # fn run() -> Result<(), ParseError> {
1544 /// let mut url = Url::parse("https://example.net?lang=fr#nav")?;
1545 /// assert_eq!(url.query(), Some("lang=fr"));
1546 ///
1547 /// url.query_pairs_mut().append_pair("foo", "bar");
1548 /// assert_eq!(url.query(), Some("lang=fr&foo=bar"));
1549 /// assert_eq!(url.as_str(), "https://example.net/?lang=fr&foo=bar#nav");
1550 ///
1551 /// url.query_pairs_mut()
1552 /// .clear()
1553 /// .append_pair("foo", "bar & baz")
1554 /// .append_pair("saisons", "\u{00C9}t\u{00E9}+hiver");
1555 /// assert_eq!(url.query(), Some("foo=bar+%26+baz&saisons=%C3%89t%C3%A9%2Bhiver"));
1556 /// assert_eq!(url.as_str(),
1557 /// "https://example.net/?foo=bar+%26+baz&saisons=%C3%89t%C3%A9%2Bhiver#nav");
1558 /// # Ok(())
1559 /// # }
1560 /// # run().unwrap();
1561 /// ```
1562 ///
1563 /// Note: `url.query_pairs_mut().clear();` is equivalent to `url.set_query(Some(""))`,
1564 /// not `url.set_query(None)`.
1565 ///
1566 /// The state of `Url` is unspecified if this return value is leaked without being dropped.
1567 pub fn query_pairs_mut(&mut self) -> form_urlencoded::Serializer<'_, UrlQuery<'_>> {
1568 let fragment = self.take_fragment();
1569
1570 let query_start;
1571 if let Some(start) = self.query_start {
1572 debug_assert!(self.byte_at(start) == b'?');
1573 query_start = start as usize;
1574 } else {
1575 query_start = self.serialization.len();
1576 self.query_start = Some(to_u32(query_start).unwrap());
1577 self.serialization.push('?');
1578 }
1579
1580 let query = UrlQuery {
1581 url: Some(self),
1582 fragment,
1583 };
1584 form_urlencoded::Serializer::for_suffix(query, query_start + "?".len())
1585 }
1586
1587 fn take_after_path(&mut self) -> String {
1588 match (self.query_start, self.fragment_start) {
1589 (Some(i), _) | (None, Some(i)) => {
1590 let after_path = self.slice(i..).to_owned();
1591 self.serialization.truncate(i as usize);
1592 after_path
1593 }
1594 (None, None) => String::new(),
1595 }
1596 }
1597
1598 /// Change this URL’s path.
1599 ///
1600 /// # Examples
1601 ///
1602 /// ```rust
1603 /// use url::Url;
1604 /// # use url::ParseError;
1605 ///
1606 /// # fn run() -> Result<(), ParseError> {
1607 /// let mut url = Url::parse("https://example.com")?;
1608 /// url.set_path("api/comments");
1609 /// assert_eq!(url.as_str(), "https://example.com/api/comments");
1610 /// assert_eq!(url.path(), "/api/comments");
1611 ///
1612 /// let mut url = Url::parse("https://example.com/api")?;
1613 /// url.set_path("data/report.csv");
1614 /// assert_eq!(url.as_str(), "https://example.com/data/report.csv");
1615 /// assert_eq!(url.path(), "/data/report.csv");
1616 ///
1617 /// // `set_path` percent-encodes the given string if it's not already percent-encoded.
1618 /// let mut url = Url::parse("https://example.com")?;
1619 /// url.set_path("api/some comments");
1620 /// assert_eq!(url.as_str(), "https://example.com/api/some%20comments");
1621 /// assert_eq!(url.path(), "/api/some%20comments");
1622 ///
1623 /// // `set_path` will not double percent-encode the string if it's already percent-encoded.
1624 /// let mut url = Url::parse("https://example.com")?;
1625 /// url.set_path("api/some%20comments");
1626 /// assert_eq!(url.as_str(), "https://example.com/api/some%20comments");
1627 /// assert_eq!(url.path(), "/api/some%20comments");
1628 ///
1629 /// # Ok(())
1630 /// # }
1631 /// # run().unwrap();
1632 /// ```
1633 pub fn set_path(&mut self, mut path: &str) {
1634 let after_path = self.take_after_path();
1635 let old_after_path_pos = to_u32(self.serialization.len()).unwrap();
1636 let cannot_be_a_base = self.cannot_be_a_base();
1637 let scheme_type = SchemeType::from(self.scheme());
1638 self.serialization.truncate(self.path_start as usize);
1639 self.mutate(|parser| {
1640 if cannot_be_a_base {
1641 if path.starts_with('/') {
1642 parser.serialization.push_str("%2F");
1643 path = &path[1..];
1644 }
1645 parser.parse_cannot_be_a_base_path(parser::Input::new(path));
1646 } else {
1647 let mut has_host = true; // FIXME
1648 parser.parse_path_start(scheme_type, &mut has_host, parser::Input::new(path));
1649 }
1650 });
1651 self.restore_after_path(old_after_path_pos, &after_path);
1652 }
1653
1654 /// Return an object with methods to manipulate this URL’s path segments.
1655 ///
1656 /// Return `Err(())` if this URL is cannot-be-a-base.
1657 #[allow(clippy::result_unit_err)]
1658 pub fn path_segments_mut(&mut self) -> Result<PathSegmentsMut<'_>, ()> {
1659 if self.cannot_be_a_base() {
1660 Err(())
1661 } else {
1662 Ok(path_segments::new(self))
1663 }
1664 }
1665
1666 fn restore_after_path(&mut self, old_after_path_position: u32, after_path: &str) {
1667 let new_after_path_position = to_u32(self.serialization.len()).unwrap();
1668 let adjust = |index: &mut u32| {
1669 *index -= old_after_path_position;
1670 *index += new_after_path_position;
1671 };
1672 if let Some(ref mut index) = self.query_start {
1673 adjust(index)
1674 }
1675 if let Some(ref mut index) = self.fragment_start {
1676 adjust(index)
1677 }
1678 self.serialization.push_str(after_path)
1679 }
1680
1681 /// Change this URL’s port number.
1682 ///
1683 /// Note that default port numbers are not reflected in the serialization.
1684 ///
1685 /// If this URL is cannot-be-a-base, does not have a host, or has the `file` scheme;
1686 /// do nothing and return `Err`.
1687 ///
1688 /// # Examples
1689 ///
1690 /// ```
1691 /// use url::Url;
1692 /// # use std::error::Error;
1693 ///
1694 /// # fn run() -> Result<(), Box<dyn Error>> {
1695 /// let mut url = Url::parse("ssh://example.net:2048/")?;
nl -ba url/src/lib.rs | sed -n '1720,1965p'
Chunk ID: 19c3fe
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2614
Output:
1720 /// # run().unwrap();
1721 /// ```
1722 ///
1723 /// Cannot set port for cannot-be-a-base URLs:
1724 ///
1725 /// ```
1726 /// use url::Url;
1727 /// # use url::ParseError;
1728 ///
1729 /// # fn run() -> Result<(), ParseError> {
1730 /// let mut url = Url::parse("mailto:rms@example.net")?;
1731 ///
1732 /// let result = url.set_port(Some(80));
1733 /// assert!(result.is_err());
1734 ///
1735 /// let result = url.set_port(None);
1736 /// assert!(result.is_err());
1737 /// # Ok(())
1738 /// # }
1739 /// # run().unwrap();
1740 /// ```
1741 #[allow(clippy::result_unit_err)]
1742 pub fn set_port(&mut self, mut port: Option<u16>) -> Result<(), ()> {
1743 // has_host implies !cannot_be_a_base
1744 if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" {
1745 return Err(());
1746 }
1747 if port.is_some() && port == parser::default_port(self.scheme()) {
1748 port = None
1749 }
1750 self.set_port_internal(port);
1751 Ok(())
1752 }
1753
1754 fn set_port_internal(&mut self, port: Option<u16>) {
1755 match (self.port, port) {
1756 (None, None) => {}
1757 (Some(_), None) => {
1758 self.serialization
1759 .drain(self.host_end as usize..self.path_start as usize);
1760 let offset = self.path_start - self.host_end;
1761 self.path_start = self.host_end;
1762 if let Some(ref mut index) = self.query_start {
1763 *index -= offset
1764 }
1765 if let Some(ref mut index) = self.fragment_start {
1766 *index -= offset
1767 }
1768 }
1769 (Some(old), Some(new)) if old == new => {}
1770 (_, Some(new)) => {
1771 let path_and_after = self.slice(self.path_start..).to_owned();
1772 self.serialization.truncate(self.host_end as usize);
1773 write!(&mut self.serialization, ":{}", new).unwrap();
1774 let old_path_start = self.path_start;
1775 let new_path_start = to_u32(self.serialization.len()).unwrap();
1776 self.path_start = new_path_start;
1777 let adjust = |index: &mut u32| {
1778 *index -= old_path_start;
1779 *index += new_path_start;
1780 };
1781 if let Some(ref mut index) = self.query_start {
1782 adjust(index)
1783 }
1784 if let Some(ref mut index) = self.fragment_start {
1785 adjust(index)
1786 }
1787 self.serialization.push_str(&path_and_after);
1788 }
1789 }
1790 self.port = port;
1791 }
1792
1793 /// Change this URL’s host.
1794 ///
1795 /// Removing the host (calling this with `None`)
1796 /// will also remove any username, password, and port number.
1797 ///
1798 /// # Examples
1799 ///
1800 /// Change host:
1801 ///
1802 /// ```
1803 /// use url::Url;
1804 /// # use url::ParseError;
1805 ///
1806 /// # fn run() -> Result<(), ParseError> {
1807 /// let mut url = Url::parse("https://example.net")?;
1808 /// let result = url.set_host(Some("rust-lang.org"));
1809 /// assert!(result.is_ok());
1810 /// assert_eq!(url.as_str(), "https://rust-lang.org/");
1811 /// # Ok(())
1812 /// # }
1813 /// # run().unwrap();
1814 /// ```
1815 ///
1816 /// Remove host:
1817 ///
1818 /// ```
1819 /// use url::Url;
1820 /// # use url::ParseError;
1821 ///
1822 /// # fn run() -> Result<(), ParseError> {
1823 /// let mut url = Url::parse("foo://example.net")?;
1824 /// let result = url.set_host(None);
1825 /// assert!(result.is_ok());
1826 /// assert_eq!(url.as_str(), "foo:/");
1827 /// # Ok(())
1828 /// # }
1829 /// # run().unwrap();
1830 /// ```
1831 ///
1832 /// Cannot remove host for 'special' schemes (e.g. `http`):
1833 ///
1834 /// ```
1835 /// use url::Url;
1836 /// # use url::ParseError;
1837 ///
1838 /// # fn run() -> Result<(), ParseError> {
1839 /// let mut url = Url::parse("https://example.net")?;
1840 /// let result = url.set_host(None);
1841 /// assert!(result.is_err());
1842 /// assert_eq!(url.as_str(), "https://example.net/");
1843 /// # Ok(())
1844 /// # }
1845 /// # run().unwrap();
1846 /// ```
1847 ///
1848 /// Cannot change or remove host for cannot-be-a-base URLs:
1849 ///
1850 /// ```
1851 /// use url::Url;
1852 /// # use url::ParseError;
1853 ///
1854 /// # fn run() -> Result<(), ParseError> {
1855 /// let mut url = Url::parse("mailto:rms@example.net")?;
1856 ///
1857 /// let result = url.set_host(Some("rust-lang.org"));
1858 /// assert!(result.is_err());
1859 /// assert_eq!(url.as_str(), "mailto:rms@example.net");
1860 ///
1861 /// let result = url.set_host(None);
1862 /// assert!(result.is_err());
1863 /// assert_eq!(url.as_str(), "mailto:rms@example.net");
1864 /// # Ok(())
1865 /// # }
1866 /// # run().unwrap();
1867 /// ```
1868 ///
1869 /// # Errors
1870 ///
1871 /// If this URL is cannot-be-a-base or there is an error parsing the given `host`,
1872 /// a [`ParseError`] variant will be returned.
1873 ///
1874 /// [`ParseError`]: enum.ParseError.html
1875 pub fn set_host(&mut self, host: Option<&str>) -> Result<(), ParseError> {
1876 if self.cannot_be_a_base() {
1877 return Err(ParseError::SetHostOnCannotBeABaseUrl);
1878 }
1879
1880 let scheme_type = SchemeType::from(self.scheme());
1881
1882 if let Some(host) = host {
1883 if host.is_empty() && scheme_type.is_special() && !scheme_type.is_file() {
1884 return Err(ParseError::EmptyHost);
1885 }
1886 let mut host_substr = host;
1887 // Otherwise, if c is U+003A (:) and the [] flag is unset, then
1888 if !host.starts_with('[') || !host.ends_with(']') {
1889 match host.find(':') {
1890 Some(0) => {
1891 // If buffer is the empty string, validation error, return failure.
1892 return Err(ParseError::InvalidDomainCharacter);
1893 }
1894 // Let host be the result of host parsing buffer
1895 Some(colon_index) => {
1896 host_substr = &host[..colon_index];
1897 }
1898 None => {}
1899 }
1900 }
1901 if SchemeType::from(self.scheme()).is_special() {
1902 self.set_host_internal(Host::parse(host_substr)?, None);
1903 } else {
1904 self.set_host_internal(Host::parse_opaque(host_substr)?, None);
1905 }
1906 } else if self.has_host() {
1907 if scheme_type.is_special() && !scheme_type.is_file() {
1908 return Err(ParseError::EmptyHost);
1909 } else if self.serialization.len() == self.path_start as usize {
1910 self.serialization.push('/');
1911 }
1912 debug_assert!(self.byte_at(self.scheme_end) == b':');
1913 debug_assert!(self.byte_at(self.path_start) == b'/');
1914
1915 let new_path_start = if scheme_type.is_file() {
1916 self.scheme_end + 3
1917 } else {
1918 self.scheme_end + 1
1919 };
1920
1921 self.serialization
1922 .drain(new_path_start as usize..self.path_start as usize);
1923 let offset = self.path_start - new_path_start;
1924 self.path_start = new_path_start;
1925 self.username_end = new_path_start;
1926 self.host_start = new_path_start;
1927 self.host_end = new_path_start;
1928 self.port = None;
1929 if let Some(ref mut index) = self.query_start {
1930 *index -= offset
1931 }
1932 if let Some(ref mut index) = self.fragment_start {
1933 *index -= offset
1934 }
1935 }
1936 Ok(())
1937 }
1938
1939 /// opt_new_port: None means leave unchanged, Some(None) means remove any port number.
1940 fn set_host_internal(&mut self, host: Host<String>, opt_new_port: Option<Option<u16>>) {
1941 let old_suffix_pos = if opt_new_port.is_some() {
1942 self.path_start
1943 } else {
1944 self.host_end
1945 };
1946 let suffix = self.slice(old_suffix_pos..).to_owned();
1947 self.serialization.truncate(self.host_start as usize);
1948 if !self.has_authority() {
1949 debug_assert!(self.slice(self.scheme_end..self.host_start) == ":");
1950 debug_assert!(self.username_end == self.host_start);
1951 self.serialization.push('/');
1952 self.serialization.push('/');
1953 self.username_end += 2;
1954 self.host_start += 2;
1955 }
1956 write!(&mut self.serialization, "{}", host).unwrap();
1957 self.host_end = to_u32(self.serialization.len()).unwrap();
1958 self.host = host.into();
1959
1960 if let Some(new_port) = opt_new_port {
1961 self.port = new_port;
1962 if let Some(port) = new_port {
1963 write!(&mut self.serialization, ":{}", port).unwrap();
1964 }
1965 }
nl -ba url/tests/unit.rs | tail -120
Chunk ID: 9bc145
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1277
Output:
1100 ),
1101 (
1102 "http://127.0.0.1:8080/test/?foo=bar#123",
1103 "http://127.0.0.1:8080/test/video",
1104 "video",
1105 ),
1106 (
1107 "http://127.0.0.1:8080/test/",
1108 "http://127.0.0.1:8080/test/video?baz=meh#456",
1109 "video?baz=meh#456",
1110 ),
1111 (
1112 "http://127.0.0.1:8080/test",
1113 "http://127.0.0.1:8080/test?baz=meh#456",
1114 "?baz=meh#456",
1115 ),
1116 (
1117 "http://127.0.0.1:8080/test/",
1118 "http://127.0.0.1:8080/test?baz=meh#456",
1119 "../test?baz=meh#456",
1120 ),
1121 (
1122 "http://127.0.0.1:8080/test/",
1123 "http://127.0.0.1:8080/test/?baz=meh#456",
1124 "?baz=meh#456",
1125 ),
1126 (
1127 "http://127.0.0.1:8080/test/?foo=bar#123",
1128 "http://127.0.0.1:8080/test/video?baz=meh#456",
1129 "video?baz=meh#456",
1130 ),
1131 (
1132 "http://127.0.0.1:8080/file.txt",
1133 "http://127.0.0.1:8080/test/file.txt",
1134 "test/file.txt",
1135 ),
1136 (
1137 "http://127.0.0.1:8080/not_equal.txt",
1138 "http://127.0.0.1:8080/test/file.txt",
1139 "test/file.txt",
1140 ),
1141 ];
1142
1143 for (base, uri, relative) in &tests {
1144 let base_uri = url::Url::parse(base).unwrap();
1145 let relative_uri = url::Url::parse(uri).unwrap();
1146 let make_relative = base_uri.make_relative(&relative_uri).unwrap();
1147 assert_eq!(
1148 make_relative, *relative,
1149 "base: {}, uri: {}, relative: {}",
1150 base, uri, relative
1151 );
1152 assert_eq!(
1153 base_uri.join(relative).unwrap().as_str(),
1154 *uri,
1155 "base: {}, uri: {}, relative: {}",
1156 base,
1157 uri,
1158 relative
1159 );
1160 }
1161
1162 let error_tests = [
1163 ("http://127.0.0.1:8080/", "https://127.0.0.1:8080/test/"),
1164 ("http://127.0.0.1:8080/", "http://127.0.0.1:8081/test/"),
1165 ("http://127.0.0.1:8080/", "http://127.0.0.2:8080/test/"),
1166 ("mailto:a@example.com", "mailto:b@example.com"),
1167 ];
1168
1169 for (base, uri) in &error_tests {
1170 let base_uri = url::Url::parse(base).unwrap();
1171 let relative_uri = url::Url::parse(uri).unwrap();
1172 let make_relative = base_uri.make_relative(&relative_uri);
1173 assert_eq!(make_relative, None, "base: {}, uri: {}", base, uri);
1174 }
1175 }
1176
1177 #[test]
1178 fn test_has_authority() {
1179 let url = Url::parse("mailto:joe@example.com").unwrap();
1180 assert!(!url.has_authority());
1181 let url = Url::parse("unix:/run/foo.socket").unwrap();
1182 assert!(!url.has_authority());
1183 let url = Url::parse("file:///tmp/foo").unwrap();
1184 assert!(url.has_authority());
1185 let url = Url::parse("http://example.com/tmp/foo").unwrap();
1186 assert!(url.has_authority());
1187 }
1188
1189 #[test]
1190 fn test_authority() {
1191 let url = Url::parse("mailto:joe@example.com").unwrap();
1192 assert_eq!(url.authority(), "");
1193 let url = Url::parse("unix:/run/foo.socket").unwrap();
1194 assert_eq!(url.authority(), "");
1195 let url = Url::parse("file:///tmp/foo").unwrap();
1196 assert_eq!(url.authority(), "");
1197 let url = Url::parse("http://example.com/tmp/foo").unwrap();
1198 assert_eq!(url.authority(), "example.com");
1199 let url = Url::parse("ftp://127.0.0.1:21/").unwrap();
1200 assert_eq!(url.authority(), "127.0.0.1");
1201 let url = Url::parse("ftp://user@127.0.0.1:2121/").unwrap();
1202 assert_eq!(url.authority(), "user@127.0.0.1:2121");
1203 let url = Url::parse("https://:@example.com/").unwrap();
1204 assert_eq!(url.authority(), "example.com");
1205 let url = Url::parse("https://:password@[::1]:8080/").unwrap();
1206 assert_eq!(url.authority(), ":password@[::1]:8080");
1207 let url = Url::parse("gopher://user:@àlex.example.com:70").unwrap();
1208 assert_eq!(url.authority(), "user@%C3%A0lex.example.com:70");
1209 let url = Url::parse("irc://àlex:àlex@àlex.рф.example.com:6667/foo").unwrap();
1210 assert_eq!(
1211 url.authority(),
1212 "%C3%A0lex:%C3%A0lex@%C3%A0lex.%D1%80%D1%84.example.com:6667"
1213 );
1214 let url = Url::parse("https://àlex:àlex@àlex.рф.example.com:443/foo").unwrap();
1215 assert_eq!(
1216 url.authority(),
1217 "%C3%A0lex:%C3%A0lex@xn--lex-8ka.xn--p1ai.example.com"
1218 );
1219 }
nl -ba url/src/parser.rs | sed -n '560,760p'
Chunk ID: dfab64
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2612
Output:
560 self.serialization.push('/');
561 self.serialization.push_str(first_segment);
562 } else if let Some(host_str) = base_url.host_str() {
563 self.serialization.push_str(host_str);
564 host_end = self.serialization.len();
565 host = base_url.host;
566 }
567 }
568 }
569 // If c is the EOF code point, U+002F (/), U+005C (\), U+003F (?), or U+0023 (#), then decrease pointer by one
570 let parse_path_input = if let Some(c) = first_char {
571 if c == '/' || c == '\\' || c == '?' || c == '#' {
572 input
573 } else {
574 input_after_first_char
575 }
576 } else {
577 input_after_first_char
578 };
579
580 let remaining =
581 self.parse_path(SchemeType::File, &mut false, host_end, parse_path_input);
582
583 let host_start = host_start as u32;
584
585 let (query_start, fragment_start) =
586 self.parse_query_and_fragment(scheme_type, scheme_end, remaining)?;
587
588 let host_end = host_end as u32;
589 return Ok(Url {
590 serialization: self.serialization,
591 scheme_end,
592 username_end: host_start,
593 host_start,
594 host_end,
595 host,
596 port: None,
597 path_start: host_end,
598 query_start,
599 fragment_start,
600 });
601 }
602 }
603 if let Some(base_url) = base_file_url {
604 match first_char {
605 None => {
606 // Copy everything except the fragment
607 let before_fragment = match base_url.fragment_start {
608 Some(i) => &base_url.serialization[..i as usize],
609 None => &*base_url.serialization,
610 };
611 self.serialization.push_str(before_fragment);
612 Ok(Url {
613 serialization: self.serialization,
614 fragment_start: None,
615 ..*base_url
616 })
617 }
618 Some('?') => {
619 // Copy everything up to the query string
620 let before_query = match (base_url.query_start, base_url.fragment_start) {
621 (None, None) => &*base_url.serialization,
622 (Some(i), _) | (None, Some(i)) => base_url.slice(..i),
623 };
624 self.serialization.push_str(before_query);
625 let (query_start, fragment_start) =
626 self.parse_query_and_fragment(scheme_type, base_url.scheme_end, input)?;
627 Ok(Url {
628 serialization: self.serialization,
629 query_start,
630 fragment_start,
631 ..*base_url
632 })
633 }
634 Some('#') => self.fragment_only(base_url, input),
635 _ => {
636 if !starts_with_windows_drive_letter_segment(&input) {
637 let before_query = match (base_url.query_start, base_url.fragment_start) {
638 (None, None) => &*base_url.serialization,
639 (Some(i), _) | (None, Some(i)) => base_url.slice(..i),
640 };
641 self.serialization.push_str(before_query);
642 self.shorten_path(SchemeType::File, base_url.path_start as usize);
643 let remaining = self.parse_path(
644 SchemeType::File,
645 &mut true,
646 base_url.path_start as usize,
647 input,
648 );
649 self.with_query_and_fragment(
650 SchemeType::File,
651 base_url.scheme_end,
652 base_url.username_end,
653 base_url.host_start,
654 base_url.host_end,
655 base_url.host,
656 base_url.port,
657 base_url.path_start,
658 remaining,
659 )
660 } else {
661 self.serialization.push_str("file:///");
662 let scheme_end = "file".len() as u32;
663 let path_start = "file://".len();
664 let remaining =
665 self.parse_path(SchemeType::File, &mut false, path_start, input);
666 let (query_start, fragment_start) =
667 self.parse_query_and_fragment(SchemeType::File, scheme_end, remaining)?;
668 let path_start = path_start as u32;
669 Ok(Url {
670 serialization: self.serialization,
671 scheme_end,
672 username_end: path_start,
673 host_start: path_start,
674 host_end: path_start,
675 host: HostInternal::None,
676 port: None,
677 path_start,
678 query_start,
679 fragment_start,
680 })
681 }
682 }
683 }
684 } else {
685 self.serialization.push_str("file:///");
686 let scheme_end = "file".len() as u32;
687 let path_start = "file://".len();
688 let remaining = self.parse_path(SchemeType::File, &mut false, path_start, input);
689 let (query_start, fragment_start) =
690 self.parse_query_and_fragment(SchemeType::File, scheme_end, remaining)?;
691 let path_start = path_start as u32;
692 Ok(Url {
693 serialization: self.serialization,
694 scheme_end,
695 username_end: path_start,
696 host_start: path_start,
697 host_end: path_start,
698 host: HostInternal::None,
699 port: None,
700 path_start,
701 query_start,
702 fragment_start,
703 })
704 }
705 }
706
707 fn parse_relative(
708 mut self,
709 input: Input<'_>,
710 scheme_type: SchemeType,
711 base_url: &Url,
712 ) -> ParseResult<Url> {
713 // relative state
714 debug_assert!(self.serialization.is_empty());
715 let (first_char, input_after_first_char) = input.split_first();
716 match first_char {
717 None => {
718 // Copy everything except the fragment
719 let before_fragment = match base_url.fragment_start {
720 Some(i) => &base_url.serialization[..i as usize],
721 None => &*base_url.serialization,
722 };
723 self.serialization.push_str(before_fragment);
724 Ok(Url {
725 serialization: self.serialization,
726 fragment_start: None,
727 ..*base_url
728 })
729 }
730 Some('?') => {
731 // Copy everything up to the query string
732 let before_query = match (base_url.query_start, base_url.fragment_start) {
733 (None, None) => &*base_url.serialization,
734 (Some(i), _) | (None, Some(i)) => base_url.slice(..i),
735 };
736 self.serialization.push_str(before_query);
737 let (query_start, fragment_start) =
738 self.parse_query_and_fragment(scheme_type, base_url.scheme_end, input)?;
739 Ok(Url {
740 serialization: self.serialization,
741 query_start,
742 fragment_start,
743 ..*base_url
744 })
745 }
746 Some('#') => self.fragment_only(base_url, input),
747 Some('/') | Some('\\') => {
748 let (slashes_count, remaining) = input.count_matching(|c| matches!(c, '/' | '\\'));
749 if slashes_count >= 2 {
750 self.log_violation_if(SyntaxViolation::ExpectedDoubleSlash, || {
751 input
752 .clone()
753 .take_while(|&c| matches!(c, '/' | '\\'))
754 .collect::<String>()
755 != "//"
756 });
757 let scheme_end = base_url.scheme_end;
758 debug_assert!(base_url.byte_at(scheme_end) == b':');
759 self.serialization
760 .push_str(base_url.slice(..scheme_end + 1));
rg "fn join|join\(" url/src/lib.rs url/src/parser.rs -nChunk ID: 5f9516
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 69
Output:
url/src/lib.rs:104:let css_url = this_document.join("../main.css")?;
url/src/lib.rs:367: /// let url = base.join("c.png")?;
url/src/lib.rs:371: /// let url = base.join("c.png")?;
url/src/lib.rs:386: pub fn join(&self, input: &str) -> Result<Url, crate::ParseError> {
nl -ba url/src/lib.rs | sed -n '320,430p'
Chunk ID: f24678
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1109
Output:
320 }
321
322 url
323 }
324
325 /// https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path
326 fn strip_trailing_spaces_from_opaque_path(&mut self) {
327 if !self.cannot_be_a_base() {
328 return;
329 }
330
331 if self.fragment_start.is_some() {
332 return;
333 }
334
335 if self.query_start.is_some() {
336 return;
337 }
338
339 let trailing_space_count = self
340 .serialization
341 .chars()
342 .rev()
343 .take_while(|c| *c == ' ')
344 .count();
345
346 let start = self.serialization.len() - trailing_space_count;
347
348 self.serialization.truncate(start);
349 }
350
351 /// Parse a string as an URL, with this URL as the base URL.
352 ///
353 /// The inverse of this is [`make_relative`].
354 ///
355 /// Note: a trailing slash is significant.
356 /// Without it, the last path component is considered to be a “file” name
357 /// to be removed to get at the “directory” that is used as the base:
358 ///
359 /// # Examples
360 ///
361 /// ```rust
362 /// use url::Url;
363 /// # use url::ParseError;
364 ///
365 /// # fn run() -> Result<(), ParseError> {
366 /// let base = Url::parse("https://example.net/a/b.html")?;
367 /// let url = base.join("c.png")?;
368 /// assert_eq!(url.as_str(), "https://example.net/a/c.png"); // Not /a/b.html/c.png
369 ///
370 /// let base = Url::parse("https://example.net/a/b/")?;
371 /// let url = base.join("c.png")?;
372 /// assert_eq!(url.as_str(), "https://example.net/a/b/c.png");
373 /// # Ok(())
374 /// # }
375 /// # run().unwrap();
376 /// ```
377 ///
378 /// # Errors
379 ///
380 /// If the function can not parse an URL from the given string
381 /// with this URL as the base URL, a [`ParseError`] variant will be returned.
382 ///
383 /// [`ParseError`]: enum.ParseError.html
384 /// [`make_relative`]: #method.make_relative
385 #[inline]
386 pub fn join(&self, input: &str) -> Result<Url, crate::ParseError> {
387 Url::options().base_url(Some(self)).parse(input)
388 }
389
390 /// Creates a relative URL if possible, with this URL as the base URL.
391 ///
392 /// This is the inverse of [`join`].
393 ///
394 /// # Examples
395 ///
396 /// ```rust
397 /// use url::Url;
398 /// # use url::ParseError;
399 ///
400 /// # fn run() -> Result<(), ParseError> {
401 /// let base = Url::parse("https://example.net/a/b.html")?;
402 /// let url = Url::parse("https://example.net/a/c.png")?;
403 /// let relative = base.make_relative(&url);
404 /// assert_eq!(relative.as_ref().map(|s| s.as_str()), Some("c.png"));
405 ///
406 /// let base = Url::parse("https://example.net/a/b/")?;
407 /// let url = Url::parse("https://example.net/a/b/c.png")?;
408 /// let relative = base.make_relative(&url);
409 /// assert_eq!(relative.as_ref().map(|s| s.as_str()), Some("c.png"));
410 ///
411 /// let base = Url::parse("https://example.net/a/b/")?;
412 /// let url = Url::parse("https://example.net/a/d/c.png")?;
413 /// let relative = base.make_relative(&url);
414 /// assert_eq!(relative.as_ref().map(|s| s.as_str()), Some("../d/c.png"));
415 ///
416 /// let base = Url::parse("https://example.net/a/b.html?c=d")?;
417 /// let url = Url::parse("https://example.net/a/b.html?e=f")?;
418 /// let relative = base.make_relative(&url);
419 /// assert_eq!(relative.as_ref().map(|s| s.as_str()), Some("?e=f"));
420 /// # Ok(())
421 /// # }
422 /// # run().unwrap();
423 /// ```
424 ///
425 /// # Errors
426 ///
427 /// If this URL can't be a base for the given URL, `None` is returned.
428 /// This is for example the case if the scheme, host or port are not the same.
429 ///
430 /// [`join`]: #method.join
nl -ba url/src/path_segments.rs | sed -n '1,280p'
Chunk ID: 7a2642
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2599
Output:
1 // Copyright 2016 The rust-url developers.
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8
9 use crate::parser::{self, to_u32, SchemeType};
10 use crate::Url;
11 use std::str;
12
13 /// Exposes methods to manipulate the path of an URL that is not cannot-be-base.
14 ///
15 /// The path always starts with a `/` slash, and is made of slash-separated segments.
16 /// There is always at least one segment (which may be the empty string).
17 ///
18 /// Examples:
19 ///
20 /// ```rust
21 /// use url::Url;
22 /// # use std::error::Error;
23 ///
24 /// # fn run() -> Result<(), Box<dyn Error>> {
25 /// let mut url = Url::parse("mailto:me@example.com")?;
26 /// assert!(url.path_segments_mut().is_err());
27 ///
28 /// let mut url = Url::parse("http://example.net/foo/index.html")?;
29 /// url.path_segments_mut().map_err(|_| "cannot be base")?
30 /// .pop().push("img").push("2/100%.png");
31 /// assert_eq!(url.as_str(), "http://example.net/foo/img/2%2F100%25.png");
32 /// # Ok(())
33 /// # }
34 /// # run().unwrap();
35 /// ```
36 #[derive(Debug)]
37 pub struct PathSegmentsMut<'a> {
38 url: &'a mut Url,
39 after_first_slash: usize,
40 after_path: String,
41 old_after_path_position: u32,
42 }
43
44 // Not re-exported outside the crate
45 pub fn new(url: &mut Url) -> PathSegmentsMut<'_> {
46 let after_path = url.take_after_path();
47 let old_after_path_position = to_u32(url.serialization.len()).unwrap();
48 // Special urls always have a non empty path
49 if SchemeType::from(url.scheme()).is_special() {
50 debug_assert!(url.byte_at(url.path_start) == b'/');
51 } else {
52 debug_assert!(
53 url.serialization.len() == url.path_start as usize
54 || url.byte_at(url.path_start) == b'/'
55 );
56 }
57 PathSegmentsMut {
58 after_first_slash: url.path_start as usize + "/".len(),
59 url,
60 old_after_path_position,
61 after_path,
62 }
63 }
64
65 impl<'a> Drop for PathSegmentsMut<'a> {
66 fn drop(&mut self) {
67 self.url
68 .restore_after_path(self.old_after_path_position, &self.after_path)
69 }
70 }
71
72 impl<'a> PathSegmentsMut<'a> {
73 /// Remove all segments in the path, leaving the minimal `url.path() == "/"`.
74 ///
75 /// Returns `&mut Self` so that method calls can be chained.
76 ///
77 /// Example:
78 ///
79 /// ```rust
80 /// use url::Url;
81 /// # use std::error::Error;
82 ///
83 /// # fn run() -> Result<(), Box<dyn Error>> {
84 /// let mut url = Url::parse("https://[code-host]/servo/rust-url/")?;
85 /// url.path_segments_mut().map_err(|_| "cannot be base")?
86 /// .clear().push("logout");
87 /// assert_eq!(url.as_str(), "https://[code-host]/logout");
88 /// # Ok(())
89 /// # }
90 /// # run().unwrap();
91 /// ```
92 pub fn clear(&mut self) -> &mut Self {
93 self.url.serialization.truncate(self.after_first_slash);
94 self
95 }
96
97 /// Remove the last segment of this URL’s path if it is empty,
98 /// except if these was only one segment to begin with.
99 ///
100 /// In other words, remove one path trailing slash, if any,
101 /// unless it is also the initial slash (so this does nothing if `url.path() == "/")`.
102 ///
103 /// Returns `&mut Self` so that method calls can be chained.
104 ///
105 /// Example:
106 ///
107 /// ```rust
108 /// use url::Url;
109 /// # use std::error::Error;
110 ///
111 /// # fn run() -> Result<(), Box<dyn Error>> {
112 /// let mut url = Url::parse("https://[code-host]/servo/rust-url/")?;
113 /// url.path_segments_mut().map_err(|_| "cannot be base")?
114 /// .push("pulls");
115 /// assert_eq!(url.as_str(), "https://[code-host]/servo/rust-url//pulls");
116 ///
117 /// let mut url = Url::parse("https://[code-host]/servo/rust-url/")?;
118 /// url.path_segments_mut().map_err(|_| "cannot be base")?
119 /// .pop_if_empty().push("pulls");
120 /// assert_eq!(url.as_str(), "https://[code-host]/servo/rust-url/pulls");
121 /// # Ok(())
122 /// # }
123 /// # run().unwrap();
124 /// ```
125 pub fn pop_if_empty(&mut self) -> &mut Self {
126 if self.after_first_slash >= self.url.serialization.len() {
127 return self;
128 }
129 if self.url.serialization[self.after_first_slash..].ends_with('/') {
130 self.url.serialization.pop();
131 }
132 self
133 }
134
135 /// Remove the last segment of this URL’s path.
136 ///
137 /// If the path only has one segment, make it empty such that `url.path() == "/"`.
138 ///
139 /// Returns `&mut Self` so that method calls can be chained.
140 pub fn pop(&mut self) -> &mut Self {
141 if self.after_first_slash >= self.url.serialization.len() {
142 return self;
143 }
144 let last_slash = self.url.serialization[self.after_first_slash..]
145 .rfind('/')
146 .unwrap_or(0);
147 self.url
148 .serialization
149 .truncate(self.after_first_slash + last_slash);
150 self
151 }
152
153 /// Append the given segment at the end of this URL’s path.
154 ///
155 /// See the documentation for `.extend()`.
156 ///
157 /// Returns `&mut Self` so that method calls can be chained.
158 pub fn push(&mut self, segment: &str) -> &mut Self {
159 self.extend(Some(segment))
160 }
161
162 /// Append each segment from the given iterator at the end of this URL’s path.
163 ///
164 /// Each segment is percent-encoded like in `Url::parse` or `Url::join`,
165 /// except that `%` and `/` characters are also encoded (to `%25` and `%2F`).
166 /// This is unlike `Url::parse` where `%` is left as-is in case some of the input
167 /// is already percent-encoded, and `/` denotes a path segment separator.)
168 ///
169 /// Note that, in addition to slashes between new segments,
170 /// this always adds a slash between the existing path and the new segments
171 /// *except* if the existing path is `"/"`.
172 /// If the previous last segment was empty (if the path had a trailing slash)
173 /// the path after `.extend()` will contain two consecutive slashes.
174 /// If that is undesired, call `.pop_if_empty()` first.
175 ///
176 /// To obtain a behavior similar to `Url::join`, call `.pop()` unconditionally first.
177 ///
178 /// Returns `&mut Self` so that method calls can be chained.
179 ///
180 /// Example:
181 ///
182 /// ```rust
183 /// use url::Url;
184 /// # use std::error::Error;
185 ///
186 /// # fn run() -> Result<(), Box<dyn Error>> {
187 /// let mut url = Url::parse("https://[code-host]/")?;
188 /// let org = "servo";
189 /// let repo = "rust-url";
190 /// let issue_number = "188";
191 /// url.path_segments_mut().map_err(|_| "cannot be base")?
192 /// .extend(&[org, repo, "issues", issue_number]);
193 /// assert_eq!(url.as_str(), "https://[code-host]/servo/rust-url/issues/188");
194 /// # Ok(())
195 /// # }
196 /// # run().unwrap();
197 /// ```
198 ///
199 /// In order to make sure that parsing the serialization of an URL gives the same URL,
200 /// a segment is ignored if it is `"."` or `".."`:
201 ///
202 /// ```rust
203 /// use url::Url;
204 /// # use std::error::Error;
205 ///
206 /// # fn run() -> Result<(), Box<dyn Error>> {
207 /// let mut url = Url::parse("https://[code-host]/servo")?;
208 /// url.path_segments_mut().map_err(|_| "cannot be base")?
209 /// .extend(&["..", "rust-url", ".", "pulls"]);
210 /// assert_eq!(url.as_str(), "https://[code-host]/servo/rust-url/pulls");
211 /// # Ok(())
212 /// # }
213 /// # run().unwrap();
214 /// ```
215 pub fn extend<I>(&mut self, segments: I) -> &mut Self
216 where
217 I: IntoIterator,
218 I::Item: AsRef<str>,
219 {
220 let scheme_type = SchemeType::from(self.url.scheme());
221 let path_start = self.url.path_start as usize;
222 self.url.mutate(|parser| {
223 parser.context = parser::Context::PathSegmentSetter;
224 for segment in segments {
225 let segment = segment.as_ref();
226 if matches!(segment, "." | "..") {
227 continue;
228 }
229 if parser.serialization.len() > path_start + 1
230 // Non special url's path might still be empty
231 || parser.serialization.len() == path_start
232 {
233 parser.serialization.push('/');
234 }
235 let mut has_host = true; // FIXME account for this?
236 parser.parse_path(
237 scheme_type,
238 &mut has_host,
239 path_start,
240 parser::Input::new(segment),
241 );
242 }
243 });
244 self
245 }
246 }
nl -ba url/src/lib.rs | sed -n '910,1025p'
Chunk ID: 2156a0
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1114
Output:
910 /// # Ok(())
911 /// # }
912 /// # run().unwrap();
913 /// ```
914 #[inline]
915 pub fn cannot_be_a_base(&self) -> bool {
916 !self.slice(self.scheme_end + 1..).starts_with('/')
917 }
918
919 /// Return the username for this URL (typically the empty string)
920 /// as a percent-encoded ASCII string.
921 ///
922 /// # Examples
923 ///
924 /// ```
925 /// use url::Url;
926 /// # use url::ParseError;
927 ///
928 /// # fn run() -> Result<(), ParseError> {
929 /// let url = Url::parse("ftp://rms@example.com")?;
930 /// assert_eq!(url.username(), "rms");
931 ///
932 /// let url = Url::parse("ftp://:secret123@example.com")?;
933 /// assert_eq!(url.username(), "");
934 ///
935 /// let url = Url::parse("https://example.com")?;
936 /// assert_eq!(url.username(), "");
937 /// # Ok(())
938 /// # }
939 /// # run().unwrap();
940 /// ```
941 pub fn username(&self) -> &str {
942 let scheme_separator_len = "://".len() as u32;
943 if self.has_authority() && self.username_end > self.scheme_end + scheme_separator_len {
944 self.slice(self.scheme_end + scheme_separator_len..self.username_end)
945 } else {
946 ""
947 }
948 }
949
950 /// Return the password for this URL, if any, as a percent-encoded ASCII string.
951 ///
952 /// # Examples
953 ///
954 /// ```
955 /// use url::Url;
956 /// # use url::ParseError;
957 ///
958 /// # fn run() -> Result<(), ParseError> {
959 /// let url = Url::parse("ftp://rms:secret123@example.com")?;
960 /// assert_eq!(url.password(), Some("secret123"));
961 ///
962 /// let url = Url::parse("ftp://:secret123@example.com")?;
963 /// assert_eq!(url.password(), Some("secret123"));
964 ///
965 /// let url = Url::parse("ftp://rms@example.com")?;
966 /// assert_eq!(url.password(), None);
967 ///
968 /// let url = Url::parse("https://example.com")?;
969 /// assert_eq!(url.password(), None);
970 /// # Ok(())
971 /// # }
972 /// # run().unwrap();
973 /// ```
974 pub fn password(&self) -> Option<&str> {
975 // This ':' is not the one marking a port number since a host can not be empty.
976 // (Except for file: URLs, which do not have port numbers.)
977 if self.has_authority()
978 && self.username_end != self.serialization.len() as u32
979 && self.byte_at(self.username_end) == b':'
980 {
981 debug_assert!(self.byte_at(self.host_start - 1) == b'@');
982 Some(self.slice(self.username_end + 1..self.host_start - 1))
983 } else {
984 None
985 }
986 }
987
988 /// Equivalent to `url.host().is_some()`.
989 ///
990 /// # Examples
991 ///
992 /// ```
993 /// use url::Url;
994 /// # use url::ParseError;
995 ///
996 /// # fn run() -> Result<(), ParseError> {
997 /// let url = Url::parse("ftp://rms@example.com")?;
998 /// assert!(url.has_host());
999 ///
1000 /// let url = Url::parse("unix:/run/foo.socket")?;
1001 /// assert!(!url.has_host());
1002 ///
1003 /// let url = Url::parse("data:text/plain,Stuff")?;
1004 /// assert!(!url.has_host());
1005 /// # Ok(())
1006 /// # }
1007 /// # run().unwrap();
1008 /// ```
1009 pub fn has_host(&self) -> bool {
1010 !matches!(self.host, HostInternal::None)
1011 }
1012
1013 /// Return the string representation of the host (domain or IP address) for this URL, if any.
1014 ///
1015 /// Non-ASCII domains are punycode-encoded per IDNA if this is the host
1016 /// of a special URL, or percent encoded for non-special URLs.
1017 /// IPv6 addresses are given between `[` and `]` brackets.
1018 ///
1019 /// Cannot-be-a-base URLs (typical of `data:` and `mailto:`) and some `file:` URLs
1020 /// don’t have a host.
1021 ///
1022 /// See also the `host` method.
1023 ///
1024 /// # Examples
1025 ///
rg "path\(\)|Position::BeforePath|Position::AfterPort|has_authority|cannot_be_a_base|join" url/tests url/src -n
Chunk ID: 8ecbba
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2213
Output:
url/tests/data.rs:58: base.join(&input)
url/src/slicing.rs:49:/// let authority: &str = &some_url[Position::BeforeUsername..Position::AfterPort];
url/src/slicing.rs:50:/// let data_url_payload: &str = &some_url[Position::BeforePath..Position::AfterQuery];
url/src/slicing.rs:109: if self.has_authority() {
url/src/slicing.rs:121: if self.has_authority() && self.byte_at(self.username_end) == b':' {
url/src/slicing.rs:130: if self.has_authority() && self.byte_at(self.username_end) == b':' {
url/src/slicing.rs:152: Position::AfterPort => self.path_start as usize,
url/src/slicing.rs:154: Position::BeforePath => self.path_start as usize,
url/src/origin.rs:18: let result = Url::parse(url.path());
url/src/quirks.rs:143: &url[Position::BeforeHost..Position::AfterPort]
url/src/quirks.rs:150: if url.cannot_be_a_base() {
url/src/quirks.rs:202: if url.cannot_be_a_base() {
url/src/quirks.rs:238: &url[Position::BeforePort..Position::AfterPort]
url/src/quirks.rs:246: // has_host implies !cannot_be_a_base
url/src/quirks.rs:268: url.path()
url/src/quirks.rs:273: if url.cannot_be_a_base() {
url/src/lib.rs:42:assert!(issue_list_url.path() == "/rust-lang/rust/issues");
url/src/lib.rs:46:assert!(&issue_list_url[Position::BeforePath..] == "/rust-lang/rust/issues?labels=E-easy&state=open");
url/src/lib.rs:48:assert!(!issue_list_url.cannot_be_a_base());
url/src/lib.rs:65:assert!(data_url.cannot_be_a_base());
url/src/lib.rs:67:assert!(data_url.path() == "text/plain,Hello");
url/src/lib.rs:96:Use the `join` method on an `Url` to use it as a base URL:
url/src/lib.rs:104:let css_url = this_document.join("../main.css")?;
url/src/lib.rs:327: if !self.cannot_be_a_base() {
url/src/lib.rs:367: /// let url = base.join("c.png")?;
url/src/lib.rs:371: /// let url = base.join("c.png")?;
url/src/lib.rs:386: pub fn join(&self, input: &str) -> Result<Url, crate::ParseError> {
url/src/lib.rs:392: /// This is the inverse of [`join`].
url/src/lib.rs:430: /// [`join`]: #method.join
url/src/lib.rs:432: if self.cannot_be_a_base() {
url/src/lib.rs:457: let (base_path, base_filename) = extract_path_filename(self.path());
url/src/lib.rs:458: let (url_path, url_filename) = extract_path_filename(url.path());
url/src/lib.rs:831: /// assert!(url.has_authority());
url/src/lib.rs:834: /// assert!(!url.has_authority());
url/src/lib.rs:837: /// assert!(!url.has_authority());
url/src/lib.rs:843: pub fn has_authority(&self) -> bool {
url/src/lib.rs:857: /// See also the `has_authority` method.
url/src/lib.rs:882: if self.has_authority() && self.path_start > self.scheme_end + scheme_separator_len {
url/src/lib.rs:903: /// assert!(!url.cannot_be_a_base());
url/src/lib.rs:906: /// assert!(!url.cannot_be_a_base());
url/src/lib.rs:909: /// assert!(url.cannot_be_a_base());
url/src/lib.rs:915: pub fn cannot_be_a_base(&self) -> bool {
url/src/lib.rs:943: if self.has_authority() && self.username_end > self.scheme_end + scheme_separator_len {
url/src/lib.rs:977: if self.has_authority()
url/src/lib.rs:1252: /// assert_eq!(url.path(), "/api/versions");
url/src/lib.rs:1255: /// assert_eq!(url.path(), "/");
url/src/lib.rs:1258: /// assert_eq!(url.path(), "/countries/vi%E1%BB%87t%20nam");
url/src/lib.rs:1311: let path = self.path();
url/src/lib.rs:1464: self.strip_trailing_spaces_from_opaque_path();
url/src/lib.rs:1529: self.strip_trailing_spaces_from_opaque_path();
url/src/lib.rs:1610: /// assert_eq!(url.path(), "/api/comments");
url/src/lib.rs:1615: /// assert_eq!(url.path(), "/data/report.csv");
url/src/lib.rs:1621: /// assert_eq!(url.path(), "/api/some%20comments");
url/src/lib.rs:1627: /// assert_eq!(url.path(), "/api/some%20comments");
url/src/lib.rs:1634: let after_path = self.take_after_path();
url/src/lib.rs:1636: let cannot_be_a_base = self.cannot_be_a_base();
url/src/lib.rs:1640: if cannot_be_a_base {
url/src/lib.rs:1645: parser.parse_cannot_be_a_base_path(parser::Input::new(path));
url/src/lib.rs:1659: if self.cannot_be_a_base() {
url/src/lib.rs:1743: // has_host implies !cannot_be_a_base
url/src/lib.rs:1876: if self.cannot_be_a_base() {
url/src/lib.rs:1948: if !self.has_authority() {
url/src/lib.rs:2021: if self.cannot_be_a_base() {
url/src/lib.rs:2061: // has_host implies !cannot_be_a_base
url/src/lib.rs:2154: // has_host implies !cannot_be_a_base
url/src/lib.rs:2326: (new_scheme_type.is_file() && self.has_authority())
url/src/lib.rs:2538: /// let path = url.to_file_path();
url/src/lib.rs:2620: .field("cannot_be_a_base", &self.cannot_be_a_base())
url/src/lib.rs:2625: .field("path", &self.path())
url/src/lib.rs:2892: "to_file_path() failed to produce an absolute Path"
url/src/lib.rs:2954: "to_file_path() failed to produce an absolute Path"
url/src/path_segments.rs:46: let after_path = url.take_after_path();
url/src/path_segments.rs:73: /// Remove all segments in the path, leaving the minimal `url.path() == "/"`.
url/src/path_segments.rs:101: /// unless it is also the initial slash (so this does nothing if `url.path() == "/")`.
url/src/path_segments.rs:137: /// If the path only has one segment, make it empty such that `url.path() == "/"`.
url/src/path_segments.rs:164: /// Each segment is percent-encoded like in `Url::parse` or `Url::join`,
url/src/path_segments.rs:176: /// To obtain a behavior similar to `Url::join`, call `.pop()` unconditionally first.
url/src/parser.rs:372: } else if base_url.cannot_be_a_base() {
url/src/parser.rs:439: debug_assert!(!base_url.cannot_be_a_base());
url/src/parser.rs:481: self.parse_cannot_be_a_base_path(input)
url/src/parser.rs:839: let has_authority = before_authority != self.serialization.len();
url/src/parser.rs:844: if host == HostInternal::None && has_authority {
url/src/parser.rs:1330: pub fn parse_cannot_be_a_base_path<'i>(&mut self, mut input: Input<'i>) -> Input<'i> {
url/tests/unit.rs:26: let url = base.join("/resources/testharness.js").unwrap();
url/tests/unit.rs:33: let url = base.join("").unwrap();
url/tests/unit.rs:38:fn test_strip_trailing_spaces_from_opaque_path() {
url/tests/unit.rs:92: assert_eq!(url.path(), $url_path);
url/tests/unit.rs:93: assert_eq!(url.to_file_path(), Ok(PathBuf::from($path)));
url/tests/unit.rs:125: assert_eq!(url.to_file_path(), Ok(PathBuf::from(os_str)));
url/tests/unit.rs:137: .to_file_path()
url/tests/unit.rs:146: assert_eq!(url.to_file_path(), Ok(PathBuf::from(r"C:\foo\bar")));
url/tests/unit.rs:158: assert_eq!(url.path(), "/foo/bar/");
url/tests/unit.rs:171: assert_eq!(url.path(), "/C:/foo/bar/");
url/tests/unit.rs:208: assert_eq!(url.path(), "/a");
url/tests/unit.rs:210: assert_eq!(url.path(), "/...");
url/tests/unit.rs:212: assert_eq!(url.path(), "/");
url/tests/unit.rs:422: Url::parse("mailto:").unwrap().cannot_be_a_base();
url/tests/unit.rs:627: assert!(no_base_url.cannot_be_a_base());
url/tests/unit.rs:636: assert_eq!(no_base_url.path(), "%2Ffoo");
url/tests/unit.rs:771:fn test_windows_unc_path() {
url/tests/unit.rs:785: let path = url.to_file_path().unwrap();
url/tests/unit.rs:830: assert_eq!(url.path(), "/path");
url/tests/unit.rs:884:fn test_url_from_file_path() {
url/tests/unit.rs:890: let path = u.to_file_path().unwrap();
url/tests/unit.rs:897:fn test_url_from_file_path() {
url/tests/unit.rs:903: let path = u.to_file_path().unwrap();
url/tests/unit.rs:908:fn test_non_special_path() {
url/tests/unit.rs:913: assert_eq!(db_url.path(), "/diesel_foo");
url/tests/unit.rs:921: assert_eq!(db_url.path(), "");
url/tests/unit.rs:924: assert_eq!(db_url.path(), "/foo");
url/tests/unit.rs:927: assert_eq!(db_url.path(), "/bar");
url/tests/unit.rs:937: assert_eq!(db_url.path(), "/");
url/tests/unit.rs:940: assert_eq!(db_url.path(), "/foo");
url/tests/unit.rs:1153: base_uri.join(relative).unwrap().as_str(),
url/tests/unit.rs:1178:fn test_has_authority() {
url/tests/unit.rs:1180: assert!(!url.has_authority());
url/tests/unit.rs:1182: assert!(!url.has_authority());
url/tests/unit.rs:1184: assert!(url.has_authority());
url/tests/unit.rs:1186: assert!(url.has_authority());
url/tests/debugger_visualizer.rs:92: let url_with_base = base_url.join("/a%2fc").unwrap();
url/tests/debugger_visualizer.rs:95: let url_with_base_replaced = base_url.join("http://[::127.0.0.1]").unwrap();
url/tests/debugger_visualizer.rs:98: let url_with_comma = base_url.join("data:text/html,test#test").unwrap();
nl -ba url/src/parser.rs | sed -n '320,430p'
Chunk ID: 2ad833
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1117
Output:
320
321 pub struct Parser<'a> {
322 pub serialization: String,
323 pub base_url: Option<&'a Url>,
324 pub query_encoding_override: EncodingOverride<'a>,
325 pub violation_fn: Option<&'a dyn Fn(SyntaxViolation)>,
326 pub context: Context,
327 }
328
329 #[derive(PartialEq, Eq, Copy, Clone)]
330 pub enum Context {
331 UrlParser,
332 Setter,
333 PathSegmentSetter,
334 }
335
336 impl<'a> Parser<'a> {
337 fn log_violation(&self, v: SyntaxViolation) {
338 if let Some(f) = self.violation_fn {
339 f(v)
340 }
341 }
342
343 fn log_violation_if(&self, v: SyntaxViolation, test: impl FnOnce() -> bool) {
344 if let Some(f) = self.violation_fn {
345 if test() {
346 f(v)
347 }
348 }
349 }
350
351 pub fn for_setter(serialization: String) -> Parser<'a> {
352 Parser {
353 serialization,
354 base_url: None,
355 query_encoding_override: None,
356 violation_fn: None,
357 context: Context::Setter,
358 }
359 }
360
361 /// https://url.spec.whatwg.org/#concept-basic-url-parser
362 pub fn parse_url(mut self, input: &str) -> ParseResult<Url> {
363 let input = Input::with_log(input, self.violation_fn);
364 if let Ok(remaining) = self.parse_scheme(input.clone()) {
365 return self.parse_with_scheme(remaining);
366 }
367
368 // No-scheme state
369 if let Some(base_url) = self.base_url {
370 if input.starts_with('#') {
371 self.fragment_only(base_url, input)
372 } else if base_url.cannot_be_a_base() {
373 Err(ParseError::RelativeUrlWithCannotBeABaseBase)
374 } else {
375 let scheme_type = SchemeType::from(base_url.scheme());
376 if scheme_type.is_file() {
377 self.parse_file(input, scheme_type, Some(base_url))
378 } else {
379 self.parse_relative(input, scheme_type, base_url)
380 }
381 }
382 } else {
383 Err(ParseError::RelativeUrlWithoutBase)
384 }
385 }
386
387 pub fn parse_scheme<'i>(&mut self, mut input: Input<'i>) -> Result<Input<'i>, ()> {
388 if input.is_empty() || !input.starts_with(ascii_alpha) {
389 return Err(());
390 }
391 debug_assert!(self.serialization.is_empty());
392 while let Some(c) = input.next() {
393 match c {
394 'a'..='z' | 'A'..='Z' | '0'..='9' | '+' | '-' | '.' => {
395 self.serialization.push(c.to_ascii_lowercase())
396 }
397 ':' => return Ok(input),
398 _ => {
399 self.serialization.clear();
400 return Err(());
401 }
402 }
403 }
404 // EOF before ':'
405 if self.context == Context::Setter {
406 Ok(input)
407 } else {
408 self.serialization.clear();
409 Err(())
410 }
411 }
412
413 fn parse_with_scheme(mut self, input: Input<'_>) -> ParseResult<Url> {
414 use crate::SyntaxViolation::{ExpectedDoubleSlash, ExpectedFileDoubleSlash};
415 let scheme_end = to_u32(self.serialization.len())?;
416 let scheme_type = SchemeType::from(&self.serialization);
417 self.serialization.push(':');
418 match scheme_type {
419 SchemeType::File => {
420 self.log_violation_if(ExpectedFileDoubleSlash, || !input.starts_with("//"));
421 let base_file_url = self.base_url.and_then(|base| {
422 if base.scheme() == "file" {
423 Some(base)
424 } else {
425 None
426 }
427 });
428 self.serialization.clear();
429 self.parse_file(input, scheme_type, base_file_url)
430 }
nl -ba url/tests/unit.rs | sed -n '1,70p'
Chunk ID: 636f5c
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 671
Output:
1 // Copyright 2013-2014 The rust-url developers.
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8
9 //! Unit tests
10
11 use std::borrow::Cow;
12 use std::cell::{Cell, RefCell};
13 use std::net::{Ipv4Addr, Ipv6Addr};
14 use std::path::{Path, PathBuf};
15 use url::{form_urlencoded, Host, Origin, Url};
16
17 #[test]
18 fn size() {
19 use std::mem::size_of;
20 assert_eq!(size_of::<Url>(), size_of::<Option<Url>>());
21 }
22
23 #[test]
24 fn test_relative() {
25 let base: Url = "sc://%C3%B1".parse().unwrap();
26 let url = base.join("/resources/testharness.js").unwrap();
27 assert_eq!(url.as_str(), "sc://%C3%B1/resources/testharness.js");
28 }
29
30 #[test]
31 fn test_relative_empty() {
32 let base: Url = "sc://%C3%B1".parse().unwrap();
33 let url = base.join("").unwrap();
34 assert_eq!(url.as_str(), "sc://%C3%B1");
35 }
36
37 #[test]
38 fn test_strip_trailing_spaces_from_opaque_path() {
39 let mut url: Url = "data:space ?query".parse().unwrap();
40 url.set_query(None);
41 assert_eq!(url.as_str(), "data:space");
42
43 let mut url: Url = "data:space #hash".parse().unwrap();
44 url.set_fragment(None);
45 assert_eq!(url.as_str(), "data:space");
46 }
47
48 #[test]
49 fn test_set_empty_host() {
50 let mut base: Url = "moz://foo:bar@servo/baz".parse().unwrap();
51 base.set_username("").unwrap();
52 assert_eq!(base.as_str(), "moz://:bar@servo/baz");
53 base.set_host(None).unwrap();
54 assert_eq!(base.as_str(), "moz:/baz");
55 base.set_host(Some("servo")).unwrap();
56 assert_eq!(base.as_str(), "moz://servo/baz");
57
58 let mut base: Url = "file://server/share/foo/bar".parse().unwrap();
59 base.set_host(None).unwrap();
60 assert_eq!(base.as_str(), "file:///share/foo/bar");
61
62 let mut base: Url = "file://server/share/foo/bar".parse().unwrap();
63 base.set_host(Some("foo")).unwrap();
64 assert_eq!(base.as_str(), "file://foo/share/foo/bar");
65 }
66
67 #[test]
68 fn test_set_empty_hostname() {
69 use url::quirks;
70 let mut base: Url = "moz://foo@servo/baz".parse().unwrap();
nl -ba url/tests/unit.rs | sed -n '900,950p'
Chunk ID: 12baef
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 547
Output:
900
901 let p = PathBuf::from("/c:/");
902 let u = Url::from_file_path(p).unwrap();
903 let path = u.to_file_path().unwrap();
904 assert_eq!("/c:/", path.to_str().unwrap());
905 }
906
907 #[test]
908 fn test_non_special_path() {
909 let mut db_url = url::Url::parse("postgres://postgres@localhost/").unwrap();
910 assert_eq!(db_url.as_str(), "postgres://postgres@localhost/");
911 db_url.set_path("diesel_foo");
912 assert_eq!(db_url.as_str(), "postgres://postgres@localhost/diesel_foo");
913 assert_eq!(db_url.path(), "/diesel_foo");
914 }
915
916 #[test]
917 fn test_non_special_path2() {
918 let mut db_url = url::Url::parse("postgres://postgres@localhost/").unwrap();
919 assert_eq!(db_url.as_str(), "postgres://postgres@localhost/");
920 db_url.set_path("");
921 assert_eq!(db_url.path(), "");
922 assert_eq!(db_url.as_str(), "postgres://postgres@localhost");
923 db_url.set_path("foo");
924 assert_eq!(db_url.path(), "/foo");
925 assert_eq!(db_url.as_str(), "postgres://postgres@localhost/foo");
926 db_url.set_path("/bar");
927 assert_eq!(db_url.path(), "/bar");
928 assert_eq!(db_url.as_str(), "postgres://postgres@localhost/bar");
929 }
930
931 #[test]
932 fn test_non_special_path3() {
933 let mut db_url = url::Url::parse("postgres://postgres@localhost/").unwrap();
934 assert_eq!(db_url.as_str(), "postgres://postgres@localhost/");
935 db_url.set_path("/");
936 assert_eq!(db_url.as_str(), "postgres://postgres@localhost/");
937 assert_eq!(db_url.path(), "/");
938 db_url.set_path("/foo");
939 assert_eq!(db_url.as_str(), "postgres://postgres@localhost/foo");
940 assert_eq!(db_url.path(), "/foo");
941 }
942
943 #[test]
944 fn test_set_scheme_to_file_with_host() {
945 let mut url: Url = "http://localhost:6767/foo/bar".parse().unwrap();
946 let result = url.set_scheme("file");
947 assert_eq!(url.to_string(), "http://localhost:6767/foo/bar");
948 assert_eq!(result, Err(()));
949 }
950
nl -ba url/src/lib.rs | sed -n '1940,2028p'
Chunk ID: f8f030
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 916
Output:
1940 fn set_host_internal(&mut self, host: Host<String>, opt_new_port: Option<Option<u16>>) {
1941 let old_suffix_pos = if opt_new_port.is_some() {
1942 self.path_start
1943 } else {
1944 self.host_end
1945 };
1946 let suffix = self.slice(old_suffix_pos..).to_owned();
1947 self.serialization.truncate(self.host_start as usize);
1948 if !self.has_authority() {
1949 debug_assert!(self.slice(self.scheme_end..self.host_start) == ":");
1950 debug_assert!(self.username_end == self.host_start);
1951 self.serialization.push('/');
1952 self.serialization.push('/');
1953 self.username_end += 2;
1954 self.host_start += 2;
1955 }
1956 write!(&mut self.serialization, "{}", host).unwrap();
1957 self.host_end = to_u32(self.serialization.len()).unwrap();
1958 self.host = host.into();
1959
1960 if let Some(new_port) = opt_new_port {
1961 self.port = new_port;
1962 if let Some(port) = new_port {
1963 write!(&mut self.serialization, ":{}", port).unwrap();
1964 }
1965 }
1966 let new_suffix_pos = to_u32(self.serialization.len()).unwrap();
1967 self.serialization.push_str(&suffix);
1968
1969 let adjust = |index: &mut u32| {
1970 *index -= old_suffix_pos;
1971 *index += new_suffix_pos;
1972 };
1973 adjust(&mut self.path_start);
1974 if let Some(ref mut index) = self.query_start {
1975 adjust(index)
1976 }
1977 if let Some(ref mut index) = self.fragment_start {
1978 adjust(index)
1979 }
1980 }
1981
1982 /// Change this URL’s host to the given IP address.
1983 ///
1984 /// If this URL is cannot-be-a-base, do nothing and return `Err`.
1985 ///
1986 /// Compared to `Url::set_host`, this skips the host parser.
1987 ///
1988 /// # Examples
1989 ///
1990 /// ```rust
1991 /// use url::{Url, ParseError};
1992 ///
1993 /// # fn run() -> Result<(), ParseError> {
1994 /// let mut url = Url::parse("http://example.com")?;
1995 /// url.set_ip_host("127.0.0.1".parse().unwrap());
1996 /// assert_eq!(url.host_str(), Some("127.0.0.1"));
1997 /// assert_eq!(url.as_str(), "http://127.0.0.1/");
1998 /// # Ok(())
1999 /// # }
2000 /// # run().unwrap();
2001 /// ```
2002 ///
2003 /// Cannot change URL's from mailto(cannot-be-base) to ip:
2004 ///
2005 /// ```rust
2006 /// use url::{Url, ParseError};
2007 ///
2008 /// # fn run() -> Result<(), ParseError> {
2009 /// let mut url = Url::parse("mailto:rms@example.com")?;
2010 /// let result = url.set_ip_host("127.0.0.1".parse().unwrap());
2011 ///
2012 /// assert_eq!(url.as_str(), "mailto:rms@example.com");
2013 /// assert!(result.is_err());
2014 /// # Ok(())
2015 /// # }
2016 /// # run().unwrap();
2017 /// ```
2018 ///
2019 #[allow(clippy::result_unit_err)]
2020 pub fn set_ip_host(&mut self, address: IpAddr) -> Result<(), ()> {
2021 if self.cannot_be_a_base() {
2022 return Err(());
2023 }
2024
2025 let address = match address {
2026 IpAddr::V4(address) => Host::Ipv4(address),
2027 IpAddr::V6(address) => Host::Ipv6(address),
2028 };
nl -ba url/src/lib.rs | sed -n '2200,2350p'
Chunk ID: e594c9
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1608
Output:
2200 }
2201 Ok(())
2202 }
2203
2204 /// Change this URL’s scheme.
2205 ///
2206 /// Do nothing and return `Err` under the following circumstances:
2207 ///
2208 /// * If the new scheme is not in `[a-zA-Z][a-zA-Z0-9+.-]+`
2209 /// * If this URL is cannot-be-a-base and the new scheme is one of
2210 /// `http`, `https`, `ws`, `wss` or `ftp`
2211 /// * If either the old or new scheme is `http`, `https`, `ws`,
2212 /// `wss` or `ftp` and the other is not one of these
2213 /// * If the new scheme is `file` and this URL includes credentials
2214 /// or has a non-null port
2215 /// * If this URL's scheme is `file` and its host is empty or null
2216 ///
2217 /// See also [the URL specification's section on legal scheme state
2218 /// overrides](https://url.spec.whatwg.org/#scheme-state).
2219 ///
2220 /// # Examples
2221 ///
2222 /// Change the URL’s scheme from `https` to `http`:
2223 ///
2224 /// ```
2225 /// use url::Url;
2226 /// # use url::ParseError;
2227 ///
2228 /// # fn run() -> Result<(), ParseError> {
2229 /// let mut url = Url::parse("https://example.net")?;
2230 /// let result = url.set_scheme("http");
2231 /// assert_eq!(url.as_str(), "http://example.net/");
2232 /// assert!(result.is_ok());
2233 /// # Ok(())
2234 /// # }
2235 /// # run().unwrap();
2236 /// ```
2237 /// Change the URL’s scheme from `foo` to `bar`:
2238 ///
2239 /// ```
2240 /// use url::Url;
2241 /// # use url::ParseError;
2242 ///
2243 /// # fn run() -> Result<(), ParseError> {
2244 /// let mut url = Url::parse("foo://example.net")?;
2245 /// let result = url.set_scheme("bar");
2246 /// assert_eq!(url.as_str(), "bar://example.net");
2247 /// assert!(result.is_ok());
2248 /// # Ok(())
2249 /// # }
2250 /// # run().unwrap();
2251 /// ```
2252 ///
2253 /// Cannot change URL’s scheme from `https` to `foõ`:
2254 ///
2255 /// ```
2256 /// use url::Url;
2257 /// # use url::ParseError;
2258 ///
2259 /// # fn run() -> Result<(), ParseError> {
2260 /// let mut url = Url::parse("https://example.net")?;
2261 /// let result = url.set_scheme("foõ");
2262 /// assert_eq!(url.as_str(), "https://example.net/");
2263 /// assert!(result.is_err());
2264 /// # Ok(())
2265 /// # }
2266 /// # run().unwrap();
2267 /// ```
2268 ///
2269 /// Cannot change URL’s scheme from `mailto` (cannot-be-a-base) to `https`:
2270 ///
2271 /// ```
2272 /// use url::Url;
2273 /// # use url::ParseError;
2274 ///
2275 /// # fn run() -> Result<(), ParseError> {
2276 /// let mut url = Url::parse("mailto:rms@example.net")?;
2277 /// let result = url.set_scheme("https");
2278 /// assert_eq!(url.as_str(), "mailto:rms@example.net");
2279 /// assert!(result.is_err());
2280 /// # Ok(())
2281 /// # }
2282 /// # run().unwrap();
2283 /// ```
2284 /// Cannot change the URL’s scheme from `foo` to `https`:
2285 ///
2286 /// ```
2287 /// use url::Url;
2288 /// # use url::ParseError;
2289 ///
2290 /// # fn run() -> Result<(), ParseError> {
2291 /// let mut url = Url::parse("foo://example.net")?;
2292 /// let result = url.set_scheme("https");
2293 /// assert_eq!(url.as_str(), "foo://example.net");
2294 /// assert!(result.is_err());
2295 /// # Ok(())
2296 /// # }
2297 /// # run().unwrap();
2298 /// ```
2299 /// Cannot change the URL’s scheme from `http` to `foo`:
2300 ///
2301 /// ```
2302 /// use url::Url;
2303 /// # use url::ParseError;
2304 ///
2305 /// # fn run() -> Result<(), ParseError> {
2306 /// let mut url = Url::parse("http://example.net")?;
2307 /// let result = url.set_scheme("foo");
2308 /// assert_eq!(url.as_str(), "http://example.net/");
2309 /// assert!(result.is_err());
2310 /// # Ok(())
2311 /// # }
2312 /// # run().unwrap();
2313 /// ```
2314 #[allow(clippy::result_unit_err, clippy::suspicious_operation_groupings)]
2315 pub fn set_scheme(&mut self, scheme: &str) -> Result<(), ()> {
2316 let mut parser = Parser::for_setter(String::new());
2317 let remaining = parser.parse_scheme(parser::Input::new(scheme))?;
2318 let new_scheme_type = SchemeType::from(&parser.serialization);
2319 let old_scheme_type = SchemeType::from(self.scheme());
2320 // If url’s scheme is a special scheme and buffer is not a special scheme, then return.
2321 if (new_scheme_type.is_special() && !old_scheme_type.is_special()) ||
2322 // If url’s scheme is not a special scheme and buffer is a special scheme, then return.
2323 (!new_scheme_type.is_special() && old_scheme_type.is_special()) ||
2324 // If url includes credentials or has a non-null port, and buffer is "file", then return.
2325 // If url’s scheme is "file" and its host is an empty host or null, then return.
2326 (new_scheme_type.is_file() && self.has_authority())
2327 {
2328 return Err(());
2329 }
2330
2331 if !remaining.is_empty() || (!self.has_host() && new_scheme_type.is_special()) {
2332 return Err(());
2333 }
2334 let old_scheme_end = self.scheme_end;
2335 let new_scheme_end = to_u32(parser.serialization.len()).unwrap();
2336 let adjust = |index: &mut u32| {
2337 *index -= old_scheme_end;
2338 *index += new_scheme_end;
2339 };
2340
2341 self.scheme_end = new_scheme_end;
2342 adjust(&mut self.username_end);
2343 adjust(&mut self.host_start);
2344 adjust(&mut self.host_end);
2345 adjust(&mut self.path_start);
2346 if let Some(ref mut index) = self.query_start {
2347 adjust(index)
2348 }
2349 if let Some(ref mut index) = self.fragment_start {
2350 adjust(index)
nl -ba url/src/lib.rs | sed -n '2028,2185p'
Chunk ID: 79fdd8
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1803
Output:
2028 };
2029 self.set_host_internal(address, None);
2030 Ok(())
2031 }
2032
2033 /// Change this URL’s password.
2034 ///
2035 /// If this URL is cannot-be-a-base or does not have a host, do nothing and return `Err`.
2036 ///
2037 /// # Examples
2038 ///
2039 /// ```rust
2040 /// use url::{Url, ParseError};
2041 ///
2042 /// # fn run() -> Result<(), ParseError> {
2043 /// let mut url = Url::parse("mailto:rmz@example.com")?;
2044 /// let result = url.set_password(Some("secret_password"));
2045 /// assert!(result.is_err());
2046 ///
2047 /// let mut url = Url::parse("ftp://user1:secret1@example.com")?;
2048 /// let result = url.set_password(Some("secret_password"));
2049 /// assert_eq!(url.password(), Some("secret_password"));
2050 ///
2051 /// let mut url = Url::parse("ftp://user2:@example.com")?;
2052 /// let result = url.set_password(Some("secret2"));
2053 /// assert!(result.is_ok());
2054 /// assert_eq!(url.password(), Some("secret2"));
2055 /// # Ok(())
2056 /// # }
2057 /// # run().unwrap();
2058 /// ```
2059 #[allow(clippy::result_unit_err)]
2060 pub fn set_password(&mut self, password: Option<&str>) -> Result<(), ()> {
2061 // has_host implies !cannot_be_a_base
2062 if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" {
2063 return Err(());
2064 }
2065 if let Some(password) = password {
2066 let host_and_after = self.slice(self.host_start..).to_owned();
2067 self.serialization.truncate(self.username_end as usize);
2068 self.serialization.push(':');
2069 self.serialization
2070 .extend(utf8_percent_encode(password, USERINFO));
2071 self.serialization.push('@');
2072
2073 let old_host_start = self.host_start;
2074 let new_host_start = to_u32(self.serialization.len()).unwrap();
2075 let adjust = |index: &mut u32| {
2076 *index -= old_host_start;
2077 *index += new_host_start;
2078 };
2079 self.host_start = new_host_start;
2080 adjust(&mut self.host_end);
2081 adjust(&mut self.path_start);
2082 if let Some(ref mut index) = self.query_start {
2083 adjust(index)
2084 }
2085 if let Some(ref mut index) = self.fragment_start {
2086 adjust(index)
2087 }
2088
2089 self.serialization.push_str(&host_and_after);
2090 } else if self.byte_at(self.username_end) == b':' {
2091 // If there is a password to remove
2092 let has_username_or_password = self.byte_at(self.host_start - 1) == b'@';
2093 debug_assert!(has_username_or_password);
2094 let username_start = self.scheme_end + 3;
2095 let empty_username = username_start == self.username_end;
2096 let start = self.username_end; // Remove the ':'
2097 let end = if empty_username {
2098 self.host_start // Remove the '@' as well
2099 } else {
2100 self.host_start - 1 // Keep the '@' to separate the username from the host
2101 };
2102 self.serialization.drain(start as usize..end as usize);
2103 let offset = end - start;
2104 self.host_start -= offset;
2105 self.host_end -= offset;
2106 self.path_start -= offset;
2107 if let Some(ref mut index) = self.query_start {
2108 *index -= offset
2109 }
2110 if let Some(ref mut index) = self.fragment_start {
2111 *index -= offset
2112 }
2113 }
2114 Ok(())
2115 }
2116
2117 /// Change this URL’s username.
2118 ///
2119 /// If this URL is cannot-be-a-base or does not have a host, do nothing and return `Err`.
2120 /// # Examples
2121 ///
2122 /// Cannot setup username from mailto(cannot-be-base)
2123 ///
2124 /// ```rust
2125 /// use url::{Url, ParseError};
2126 ///
2127 /// # fn run() -> Result<(), ParseError> {
2128 /// let mut url = Url::parse("mailto:rmz@example.com")?;
2129 /// let result = url.set_username("user1");
2130 /// assert_eq!(url.as_str(), "mailto:rmz@example.com");
2131 /// assert!(result.is_err());
2132 /// # Ok(())
2133 /// # }
2134 /// # run().unwrap();
2135 /// ```
2136 ///
2137 /// Setup username to user1
2138 ///
2139 /// ```rust
2140 /// use url::{Url, ParseError};
2141 ///
2142 /// # fn run() -> Result<(), ParseError> {
2143 /// let mut url = Url::parse("ftp://:secre1@example.com/")?;
2144 /// let result = url.set_username("user1");
2145 /// assert!(result.is_ok());
2146 /// assert_eq!(url.username(), "user1");
2147 /// assert_eq!(url.as_str(), "ftp://user1:secre1@example.com/");
2148 /// # Ok(())
2149 /// # }
2150 /// # run().unwrap();
2151 /// ```
2152 #[allow(clippy::result_unit_err)]
2153 pub fn set_username(&mut self, username: &str) -> Result<(), ()> {
2154 // has_host implies !cannot_be_a_base
2155 if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" {
2156 return Err(());
2157 }
2158 let username_start = self.scheme_end + 3;
2159 debug_assert!(self.slice(self.scheme_end..username_start) == "://");
2160 if self.slice(username_start..self.username_end) == username {
2161 return Ok(());
2162 }
2163 let after_username = self.slice(self.username_end..).to_owned();
2164 self.serialization.truncate(username_start as usize);
2165 self.serialization
2166 .extend(utf8_percent_encode(username, USERINFO));
2167
2168 let mut removed_bytes = self.username_end;
2169 self.username_end = to_u32(self.serialization.len()).unwrap();
2170 let mut added_bytes = self.username_end;
2171
2172 let new_username_is_empty = self.username_end == username_start;
2173 match (new_username_is_empty, after_username.chars().next()) {
2174 (true, Some('@')) => {
2175 removed_bytes += 1;
2176 self.serialization.push_str(&after_username[1..]);
2177 }
2178 (false, Some('@')) | (_, Some(':')) | (true, _) => {
2179 self.serialization.push_str(&after_username);
2180 }
2181 (false, _) => {
2182 added_bytes += 1;
2183 self.serialization.push('@');
2184 self.serialization.push_str(&after_username);
2185 }
nl -ba url/src/lib.rs | sed -n '2350,2385p'
Chunk ID: 6545be
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 337
Output:
2350 adjust(index)
2351 }
2352
2353 parser.serialization.push_str(self.slice(old_scheme_end..));
2354 self.serialization = parser.serialization;
2355
2356 // Update the port so it can be removed
2357 // If it is the scheme's default
2358 // we don't mind it silently failing
2359 // if there was no port in the first place
2360 let previous_port = self.port();
2361 let _ = self.set_port(previous_port);
2362
2363 Ok(())
2364 }
2365
2366 /// Convert a file name as `std::path::Path` into an URL in the `file` scheme.
2367 ///
2368 /// This returns `Err` if the given path is not absolute or,
2369 /// on Windows, if the prefix is not a disk prefix (e.g. `C:`) or a UNC prefix (`\\`).
2370 ///
2371 /// # Examples
2372 ///
2373 /// On Unix-like platforms:
2374 ///
2375 /// ```
2376 /// # if cfg!(unix) {
2377 /// use url::Url;
2378 ///
2379 /// # fn run() -> Result<(), ()> {
2380 /// let url = Url::from_file_path("/tmp/foo.txt")?;
2381 /// assert_eq!(url.as_str(), "file:///tmp/foo.txt");
2382 ///
2383 /// let url = Url::from_file_path("../foo.txt");
2384 /// assert!(url.is_err());
2385 ///
nl -ba url/src/lib.rs | sed -n '432,520p'
Chunk ID: 3d37d8
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 866
Output:
432 if self.cannot_be_a_base() {
433 return None;
434 }
435
436 // Scheme, host and port need to be the same
437 if self.scheme() != url.scheme() || self.host() != url.host() || self.port() != url.port() {
438 return None;
439 }
440
441 // We ignore username/password at this point
442
443 // The path has to be transformed
444 let mut relative = String::new();
445
446 // Extract the filename of both URIs, these need to be handled separately
447 fn extract_path_filename(s: &str) -> (&str, &str) {
448 let last_slash_idx = s.rfind('/').unwrap_or(0);
449 let (path, filename) = s.split_at(last_slash_idx);
450 if filename.is_empty() {
451 (path, "")
452 } else {
453 (path, &filename[1..])
454 }
455 }
456
457 let (base_path, base_filename) = extract_path_filename(self.path());
458 let (url_path, url_filename) = extract_path_filename(url.path());
459
460 let mut base_path = base_path.split('/').peekable();
461 let mut url_path = url_path.split('/').peekable();
462
463 // Skip over the common prefix
464 while base_path.peek().is_some() && base_path.peek() == url_path.peek() {
465 base_path.next();
466 url_path.next();
467 }
468
469 // Add `..` segments for the remainder of the base path
470 for base_path_segment in base_path {
471 // Skip empty last segments
472 if base_path_segment.is_empty() {
473 break;
474 }
475
476 if !relative.is_empty() {
477 relative.push('/');
478 }
479
480 relative.push_str("..");
481 }
482
483 // Append the remainder of the other URI
484 for url_path_segment in url_path {
485 if !relative.is_empty() {
486 relative.push('/');
487 }
488
489 relative.push_str(url_path_segment);
490 }
491
492 // Add the filename if they are not the same
493 if !relative.is_empty() || base_filename != url_filename {
494 // If the URIs filename is empty this means that it was a directory
495 // so we'll have to append a '/'.
496 //
497 // Otherwise append it directly as the new filename.
498 if url_filename.is_empty() {
499 relative.push('/');
500 } else {
501 if !relative.is_empty() {
502 relative.push('/');
503 }
504 relative.push_str(url_filename);
505 }
506 }
507
508 // Query and fragment are only taken from the other URI
509 if let Some(query) = url.query() {
510 relative.push('?');
511 relative.push_str(query);
512 }
513
514 if let Some(fragment) = url.fragment() {
515 relative.push('#');
516 relative.push_str(fragment);
517 }
518
519 Some(relative)
520 }
*** Begin Patch
*** Update File: /app/url/src/lib.rs
@@
use std::str;
use std::string::String;
use std::{fmt, io, mem};
+const PATH_PREFIX_MARKER: &str = "/.";
+
/// A parsed URL record.
#[derive(Clone)]
pub struct Url {
@@
fn strip_trailing_spaces_from_opaque_path(&mut self) {
if !self.cannot_be_a_base() {
return;
}
@@
self.serialization.truncate(start);
}
+
+ fn is_non_special_without_authority(&self) -> bool {
+ SchemeType::from(self.scheme()) == SchemeType::NotSpecial
+ && matches!(self.host, HostInternal::None)
+ && self.port.is_none()
+ && self.username_end == self.path_start
+ && self.host_start == self.path_start
+ && self.host_end == self.path_start
+ }
+
+ fn path_prefix_marker_end(&self) -> Option<u32> {
+ if self.is_non_special_without_authority()
+ && self
+ .slice(self.path_start..)
+ .starts_with(&format!("{}//", PATH_PREFIX_MARKER))
+ {
+ Some(self.path_start + PATH_PREFIX_MARKER.len() as u32)
+ } else {
+ None
+ }
+ }
+
+ pub(crate) fn logical_path_start(&self) -> u32 {
+ self.path_prefix_marker_end().unwrap_or(self.path_start)
+ }
+
+ fn adjust_after_path_prefix_marker(&mut self, offset: i32) {
+ let adjust = |index: &mut u32| {
+ if offset >= 0 {
+ *index += offset as u32;
+ } else {
+ *index -= (-offset) as u32;
+ }
+ };
+ if let Some(ref mut index) = self.query_start {
+ adjust(index)
+ }
+ if let Some(ref mut index) = self.fragment_start {
+ adjust(index)
+ }
+ }
+
+ fn remove_path_prefix_marker(&mut self) {
+ if let Some(marker_end) = self.path_prefix_marker_end() {
+ self.serialization
+ .drain(self.path_start as usize..marker_end as usize);
+ self.adjust_after_path_prefix_marker(-(PATH_PREFIX_MARKER.len() as i32));
+ }
+ }
+
+ fn recompute_path_prefix_marker(&mut self) {
+ self.remove_path_prefix_marker();
+ if self.is_non_special_without_authority() && self.slice(self.path_start..).starts_with("//")
+ {
+ self.serialization
+ .insert_str(self.path_start as usize, PATH_PREFIX_MARKER);
+ self.adjust_after_path_prefix_marker(PATH_PREFIX_MARKER.len() as i32);
+ }
+ }
/// Parse a string as an URL, with this URL as the base URL.
@@
pub fn path(&self) -> &str {
+ let path_start = self.logical_path_start();
match (self.query_start, self.fragment_start) {
- (None, None) => self.slice(self.path_start..),
+ (None, None) => self.slice(path_start..),
(Some(next_component_start), _) | (None, Some(next_component_start)) => {
- self.slice(self.path_start..next_component_start)
+ self.slice(path_start..next_component_start)
}
}
}
@@
self.restore_after_path(old_after_path_pos, &after_path);
+ self.recompute_path_prefix_marker();
}
@@
self.fragment_start = Some(to_u32(self.serialization.len()).unwrap());
self.serialization.push('#');
self.serialization.push_str(fragment);
@@
if let Some(ref mut index) = self.fragment_start {
*index -= offset
}
+ self.recompute_path_prefix_marker();
}
Ok(())
}
@@
let old_suffix_pos = if opt_new_port.is_some() {
- self.path_start
+ self.logical_path_start()
} else {
self.host_end
};
*** End Patch
apply_patch verification failed: Failed to find expected lines in /app/url/src/lib.rs:
use std::str;
use std::string::String;
use std::{fmt, io, mem};
/// A parsed URL record.
#[derive(Clone)]
pub struct Url {nl -ba url/src/lib.rs | sed -n '120,170p'
Chunk ID: c19d0e
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 384
Output:
120 ```
121
122 */
123
124 #![doc(html_root_url = "https://docs.rs/url/2.3.1")]
125 #![cfg_attr(
126 feature = "debugger_visualizer",
127 feature(debugger_visualizer),
128 debugger_visualizer(natvis_file = "../../debug_metadata/url.natvis")
129 )]
130
131 pub use form_urlencoded;
132
133 #[cfg(feature = "serde")]
134 extern crate serde;
135
136 use crate::host::HostInternal;
137 use crate::parser::{to_u32, Context, Parser, SchemeType, PATH_SEGMENT, USERINFO};
138 use percent_encoding::{percent_decode, percent_encode, utf8_percent_encode};
139 use std::borrow::Borrow;
140 use std::cmp;
141 use std::fmt::{self, Write};
142 use std::hash;
143 use std::io;
144 use std::mem;
145 use std::net::{IpAddr, SocketAddr, ToSocketAddrs};
146 use std::ops::{Range, RangeFrom, RangeTo};
147 use std::path::{Path, PathBuf};
148 use std::str;
149
150 use std::convert::TryFrom;
151
152 pub use crate::host::Host;
153 pub use crate::origin::{OpaqueOrigin, Origin};
154 pub use crate::parser::{ParseError, SyntaxViolation};
155 pub use crate::path_segments::PathSegmentsMut;
156 pub use crate::slicing::Position;
157 pub use form_urlencoded::EncodingOverride;
158
159 mod host;
160 mod origin;
161 mod parser;
162 mod path_segments;
163 mod slicing;
164
165 #[doc(hidden)]
166 pub mod quirks;
167
168 /// A parsed URL record.
169 #[derive(Clone)]
170 pub struct Url {
nl -ba url/src/lib.rs | sed -n '318,355p'
Chunk ID: 489f34
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 306
Output:
318 if let Ok(ref mut url) = url {
319 url.query_pairs_mut().extend_pairs(iter);
320 }
321
322 url
323 }
324
325 /// https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path
326 fn strip_trailing_spaces_from_opaque_path(&mut self) {
327 if !self.cannot_be_a_base() {
328 return;
329 }
330
331 if self.fragment_start.is_some() {
332 return;
333 }
334
335 if self.query_start.is_some() {
336 return;
337 }
338
339 let trailing_space_count = self
340 .serialization
341 .chars()
342 .rev()
343 .take_while(|c| *c == ' ')
344 .count();
345
346 let start = self.serialization.len() - trailing_space_count;
347
348 self.serialization.truncate(start);
349 }
350
351 /// Parse a string as an URL, with this URL as the base URL.
352 ///
353 /// The inverse of this is [`make_relative`].
354 ///
355 /// Note: a trailing slash is significant.
nl -ba url/src/lib.rs | sed -n '1258,1272p'
Chunk ID: c2cff2
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 154
Output:
1258 /// assert_eq!(url.path(), "/countries/vi%E1%BB%87t%20nam");
1259 /// # Ok(())
1260 /// # }
1261 /// # run().unwrap();
1262 /// ```
1263 pub fn path(&self) -> &str {
1264 match (self.query_start, self.fragment_start) {
1265 (None, None) => self.slice(self.path_start..),
1266 (Some(next_component_start), _) | (None, Some(next_component_start)) => {
1267 self.slice(self.path_start..next_component_start)
1268 }
1269 }
1270 }
1271
1272 /// Unless this URL is cannot-be-a-base,
nl -ba url/src/lib.rs | sed -n '1630,1655p'
Chunk ID: bb5d7f
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 310
Output:
1630 /// # }
1631 /// # run().unwrap();
1632 /// ```
1633 pub fn set_path(&mut self, mut path: &str) {
1634 let after_path = self.take_after_path();
1635 let old_after_path_pos = to_u32(self.serialization.len()).unwrap();
1636 let cannot_be_a_base = self.cannot_be_a_base();
1637 let scheme_type = SchemeType::from(self.scheme());
1638 self.serialization.truncate(self.path_start as usize);
1639 self.mutate(|parser| {
1640 if cannot_be_a_base {
1641 if path.starts_with('/') {
1642 parser.serialization.push_str("%2F");
1643 path = &path[1..];
1644 }
1645 parser.parse_cannot_be_a_base_path(parser::Input::new(path));
1646 } else {
1647 let mut has_host = true; // FIXME
1648 parser.parse_path_start(scheme_type, &mut has_host, parser::Input::new(path));
1649 }
1650 });
1651 self.restore_after_path(old_after_path_pos, &after_path);
1652 }
1653
1654 /// Return an object with methods to manipulate this URL’s path segments.
1655 ///
nl -ba url/src/lib.rs | sed -n '1900,1938p'
Chunk ID: 9ecf01
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 440
Output:
1900 }
1901 if SchemeType::from(self.scheme()).is_special() {
1902 self.set_host_internal(Host::parse(host_substr)?, None);
1903 } else {
1904 self.set_host_internal(Host::parse_opaque(host_substr)?, None);
1905 }
1906 } else if self.has_host() {
1907 if scheme_type.is_special() && !scheme_type.is_file() {
1908 return Err(ParseError::EmptyHost);
1909 } else if self.serialization.len() == self.path_start as usize {
1910 self.serialization.push('/');
1911 }
1912 debug_assert!(self.byte_at(self.scheme_end) == b':');
1913 debug_assert!(self.byte_at(self.path_start) == b'/');
1914
1915 let new_path_start = if scheme_type.is_file() {
1916 self.scheme_end + 3
1917 } else {
1918 self.scheme_end + 1
1919 };
1920
1921 self.serialization
1922 .drain(new_path_start as usize..self.path_start as usize);
1923 let offset = self.path_start - new_path_start;
1924 self.path_start = new_path_start;
1925 self.username_end = new_path_start;
1926 self.host_start = new_path_start;
1927 self.host_end = new_path_start;
1928 self.port = None;
1929 if let Some(ref mut index) = self.query_start {
1930 *index -= offset
1931 }
1932 if let Some(ref mut index) = self.fragment_start {
1933 *index -= offset
1934 }
1935 }
1936 Ok(())
1937 }
1938
*** Begin Patch
*** Update File: /app/url/src/lib.rs
@@
pub mod quirks;
+const PATH_PREFIX_MARKER: &str = "/.";
+
/// A parsed URL record.
#[derive(Clone)]
pub struct Url {
@@
fn strip_trailing_spaces_from_opaque_path(&mut self) {
if !self.cannot_be_a_base() {
return;
}
@@
self.serialization.truncate(start);
}
+
+ fn is_non_special_without_authority(&self) -> bool {
+ SchemeType::from(self.scheme()) == SchemeType::NotSpecial
+ && matches!(self.host, HostInternal::None)
+ && self.port.is_none()
+ && self.username_end == self.path_start
+ && self.host_start == self.path_start
+ && self.host_end == self.path_start
+ }
+
+ fn path_prefix_marker_end(&self) -> Option<u32> {
+ if self.is_non_special_without_authority()
+ && self
+ .slice(self.path_start..)
+ .starts_with(&format!("{}//", PATH_PREFIX_MARKER))
+ {
+ Some(self.path_start + PATH_PREFIX_MARKER.len() as u32)
+ } else {
+ None
+ }
+ }
+
+ pub(crate) fn logical_path_start(&self) -> u32 {
+ self.path_prefix_marker_end().unwrap_or(self.path_start)
+ }
+
+ fn adjust_after_path_prefix_marker(&mut self, offset: i32) {
+ let adjust = |index: &mut u32| {
+ if offset >= 0 {
+ *index += offset as u32;
+ } else {
+ *index -= (-offset) as u32;
+ }
+ };
+ if let Some(ref mut index) = self.query_start {
+ adjust(index)
+ }
+ if let Some(ref mut index) = self.fragment_start {
+ adjust(index)
+ }
+ }
+
+ pub(crate) fn remove_path_prefix_marker(&mut self) {
+ if let Some(marker_end) = self.path_prefix_marker_end() {
+ self.serialization
+ .drain(self.path_start as usize..marker_end as usize);
+ self.adjust_after_path_prefix_marker(-(PATH_PREFIX_MARKER.len() as i32));
+ }
+ }
+
+ pub(crate) fn recompute_path_prefix_marker(&mut self) {
+ self.remove_path_prefix_marker();
+ if self.is_non_special_without_authority() && self.slice(self.path_start..).starts_with("//")
+ {
+ self.serialization
+ .insert_str(self.path_start as usize, PATH_PREFIX_MARKER);
+ self.adjust_after_path_prefix_marker(PATH_PREFIX_MARKER.len() as i32);
+ }
+ }
/// Parse a string as an URL, with this URL as the base URL.
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/src/lib.rs
*** Begin Patch
*** Update File: /app/url/src/lib.rs
@@
/// ```
pub fn path(&self) -> &str {
+ let path_start = self.logical_path_start();
match (self.query_start, self.fragment_start) {
- (None, None) => self.slice(self.path_start..),
+ (None, None) => self.slice(path_start..),
(Some(next_component_start), _) | (None, Some(next_component_start)) => {
- self.slice(self.path_start..next_component_start)
+ self.slice(path_start..next_component_start)
}
}
}
@@
});
self.restore_after_path(old_after_path_pos, &after_path);
+ self.recompute_path_prefix_marker();
}
@@
if let Some(ref mut index) = self.fragment_start {
*index -= offset
}
+ self.recompute_path_prefix_marker();
}
Ok(())
}
@@
/// opt_new_port: None means leave unchanged, Some(None) means remove any port number.
fn set_host_internal(&mut self, host: Host<String>, opt_new_port: Option<Option<u16>>) {
let old_suffix_pos = if opt_new_port.is_some() {
- self.path_start
+ self.logical_path_start()
} else {
self.host_end
};
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/src/lib.rs
*** Begin Patch
*** Update File: /app/url/src/lib.rs
@@
/// opt_new_port: None means leave unchanged, Some(None) means remove any port number.
fn set_host_internal(&mut self, host: Host<String>, opt_new_port: Option<Option<u16>>) {
let old_suffix_pos = if opt_new_port.is_some() {
self.logical_path_start()
+ } else if !self.has_authority() {
+ self.logical_path_start()
} else {
self.host_end
};
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/src/lib.rs
*** Begin Patch
*** Update File: /app/url/src/parser.rs
@@
_ => {
let before_query = match (base_url.query_start, base_url.fragment_start) {
- (None, None) => &*base_url.serialization,
- (Some(i), _) | (None, Some(i)) => base_url.slice(..i),
+ (None, None) => base_url.slice(..base_url.logical_path_start()),
+ (Some(_), _) | (None, Some(_)) => base_url.slice(..base_url.logical_path_start()),
};
self.serialization.push_str(before_query);
+ let before_query_end = match (base_url.query_start, base_url.fragment_start) {
+ (None, None) => base_url.serialization.len() as u32,
+ (Some(i), _) | (None, Some(i)) => i,
+ };
+ self.serialization
+ .push_str(base_url.slice(base_url.logical_path_start()..before_query_end));
// FIXME spec says just "remove last entry", not the "pop" algorithm
self.pop_path(scheme_type, base_url.path_start as usize);
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/src/parser.rs
*** Begin Patch
*** Update File: /app/url/src/parser.rs
@@
remaining: Input<'_>,
) -> ParseResult<Url> {
let (query_start, fragment_start) =
self.parse_query_and_fragment(scheme_type, scheme_end, remaining)?;
- Ok(Url {
+ let mut url = Url {
serialization: self.serialization,
scheme_end,
username_end,
host_start,
host_end,
@@
path_start,
query_start,
fragment_start,
- })
+ };
+ url.recompute_path_prefix_marker();
+ Ok(url)
}
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/src/parser.rs
*** Begin Patch
*** Update File: /app/url/src/slicing.rs
@@
Position::AfterPort => self.path_start as usize,
- Position::BeforePath => self.path_start as usize,
+ Position::BeforePath => self.logical_path_start() as usize,
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/src/slicing.rs
*** Begin Patch
*** Update File: /app/url/src/path_segments.rs
@@
pub fn new(url: &mut Url) -> PathSegmentsMut<'_> {
let after_path = url.take_after_path();
let old_after_path_position = to_u32(url.serialization.len()).unwrap();
+ url.remove_path_prefix_marker();
// Special urls always have a non empty path
if SchemeType::from(url.scheme()).is_special() {
debug_assert!(url.byte_at(url.path_start) == b'/');
@@
impl<'a> Drop for PathSegmentsMut<'a> {
fn drop(&mut self) {
self.url
- .restore_after_path(self.old_after_path_position, &self.after_path)
+ .restore_after_path(self.old_after_path_position, &self.after_path);
+ self.url.recompute_path_prefix_marker();
}
}
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/src/path_segments.rs
*** Begin Patch
*** Update File: /app/url/src/lib.rs
@@
const PATH_PREFIX_MARKER: &str = "/.";
+const PATH_PREFIX_MARKER_WITH_LOGICAL_PATH: &str = "/.//";
@@
if self.is_non_special_without_authority()
&& self
.slice(self.path_start..)
- .starts_with(&format!("{}//", PATH_PREFIX_MARKER))
+ .starts_with(PATH_PREFIX_MARKER_WITH_LOGICAL_PATH)
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/src/lib.rs
*** Begin Patch
*** Update File: /app/url/tests/unit.rs
@@
-use url::{form_urlencoded, Host, Origin, Url};
+use url::{form_urlencoded, Host, Origin, Position, Url};
@@
fn test_non_special_path3() {
let mut db_url = url::Url::parse("postgres://postgres@localhost/").unwrap();
assert_eq!(db_url.as_str(), "postgres://postgres@localhost/");
db_url.set_path("/");
assert_eq!(db_url.as_str(), "postgres://postgres@localhost/");
@@
assert_eq!(db_url.as_str(), "postgres://postgres@localhost/foo");
assert_eq!(db_url.path(), "/foo");
}
+
+#[test]
+fn test_non_special_path_prefix_marker() {
+ let url = Url::parse("foo:/.//path?query#fragment").unwrap();
+ assert_eq!(url.as_str(), "foo:/.//path?query#fragment");
+ assert!(!url.has_authority());
+ assert_eq!(url.host_str(), None);
+ assert_eq!(url.path(), "//path");
+ assert_eq!(&url[Position::AfterPort..Position::BeforePath], "/.");
+ assert_eq!(&url[Position::BeforePath..Position::AfterPath], "//path");
+
+ let reparsed = Url::parse(url.as_str()).unwrap();
+ assert_eq!(reparsed.as_str(), url.as_str());
+ assert_eq!(reparsed.host_str(), None);
+ assert_eq!(reparsed.path(), "//path");
+}
+
+#[test]
+fn test_non_special_path_prefix_marker_after_normalization() {
+ let url = Url::parse("foo:/..//path?query#fragment").unwrap();
+ assert_eq!(url.as_str(), "foo:/.//path?query#fragment");
+ assert_eq!(url.path(), "//path");
+}
+
+#[test]
+fn test_non_special_path_prefix_marker_join() {
+ let base = Url::parse("foo:/.//a/b?old#fragment").unwrap();
+
+ let child = base.join("c").unwrap();
+ assert_eq!(child.as_str(), "foo:/.//a/c");
+ assert_eq!(child.path(), "//a/c");
+
+ let query = base.join("?new").unwrap();
+ assert_eq!(query.as_str(), "foo:/.//a/b?new");
+ assert_eq!(query.path(), "//a/b");
+
+ let fragment = base.join("#new").unwrap();
+ assert_eq!(fragment.as_str(), "foo:/.//a/b?old#new");
+ assert_eq!(fragment.path(), "//a/b");
+}
+
+#[test]
+fn test_non_special_path_prefix_marker_setters() {
+ let mut url = Url::parse("foo:/a").unwrap();
+ url.set_path("//path");
+ assert_eq!(url.as_str(), "foo:/.//path");
+ assert_eq!(url.path(), "//path");
+
+ url.path_segments_mut().unwrap().clear().push("single");
+ assert_eq!(url.as_str(), "foo:/single");
+ assert_eq!(url.path(), "/single");
+
+ let mut host_url = Url::parse("foo://host//path").unwrap();
+ host_url.set_host(None).unwrap();
+ assert_eq!(host_url.as_str(), "foo:/.//path");
+ assert_eq!(host_url.host_str(), None);
+ assert_eq!(host_url.path(), "//path");
+}
+
+#[test]
+fn test_non_special_path_prefix_marker_unaffected_urls() {
+ let host_url = Url::parse("foo://host//path").unwrap();
+ assert_eq!(host_url.as_str(), "foo://host//path");
+ assert!(host_url.has_authority());
+ assert_eq!(host_url.path(), "//path");
+
+ let special_url = Url::parse("http://example.com//path").unwrap();
+ assert_eq!(special_url.as_str(), "http://example.com//path");
+ assert_eq!(special_url.path(), "//path");
+}
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/tests/unit.rs
cargo test -p url --test unit non_special_path_prefix_marker --locked
Chunk ID: 146cab
Wall time: 2.0574 seconds
Process exited with code 101
Original token count: 769
Output:
Compiling proc-macro2 v1.0.106
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> percent_encoding/src/lib.rs:462:35
|
462 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
462 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
462 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `percent-encoding` (lib) generated 1 warning
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> form_urlencoded/src/lib.rs:403:35
|
403 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
403 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
403 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `form_urlencoded` (lib) generated 1 warning
Compiling url v2.3.1 (/app/url)
Compiling quote v1.0.45
Compiling syn v1.0.109
Compiling debugger_test v0.1.5
Finished `test` profile [unoptimized + debuginfo] target(s) in 2.18s
Running tests/unit.rs (target/debug/deps/unit-a494890561398094)
running 5 tests
test test_non_special_path_prefix_marker ... ok
test test_non_special_path_prefix_marker_after_normalization ... ok
test test_non_special_path_prefix_marker_join ... ok
test test_non_special_path_prefix_marker_setters ... FAILED
test test_non_special_path_prefix_marker_unaffected_urls ... ok
failures:
---- test_non_special_path_prefix_marker_setters stdout ----
thread 'test_non_special_path_prefix_marker_setters' panicked at url/tests/unit.rs:996:5:
assertion `left == right` failed
left: "foo://path"
right: "foo:/.//path"
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
test_non_special_path_prefix_marker_setters
test result: FAILED. 4 passed; 1 failed; 0 ignored; 0 measured; 58 filtered out; finished in 0.00s
error: test failed, to rerun pass `-p url --test unit`
nl -ba url/tests/unit.rs | sed -n '986,1002p'
Chunk ID: 69d698
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 172
Output:
986 url.set_path("//path");
987 assert_eq!(url.as_str(), "foo:/.//path");
988 assert_eq!(url.path(), "//path");
989
990 url.path_segments_mut().unwrap().clear().push("single");
991 assert_eq!(url.as_str(), "foo:/single");
992 assert_eq!(url.path(), "/single");
993
994 let mut host_url = Url::parse("foo://host//path").unwrap();
995 host_url.set_host(None).unwrap();
996 assert_eq!(host_url.as_str(), "foo:/.//path");
997 assert_eq!(host_url.host_str(), None);
998 assert_eq!(host_url.path(), "//path");
999 }
1000
1001 #[test]
1002 fn test_non_special_path_prefix_marker_unaffected_urls() {
nl -ba url/src/lib.rs | sed -n '1935,1978p'
Chunk ID: 13b14a
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 577
Output:
1935 /// If this URL is cannot-be-a-base or there is an error parsing the given `host`,
1936 /// a [`ParseError`] variant will be returned.
1937 ///
1938 /// [`ParseError`]: enum.ParseError.html
1939 pub fn set_host(&mut self, host: Option<&str>) -> Result<(), ParseError> {
1940 if self.cannot_be_a_base() {
1941 return Err(ParseError::SetHostOnCannotBeABaseUrl);
1942 }
1943
1944 let scheme_type = SchemeType::from(self.scheme());
1945
1946 if let Some(host) = host {
1947 if host.is_empty() && scheme_type.is_special() && !scheme_type.is_file() {
1948 return Err(ParseError::EmptyHost);
1949 }
1950 let mut host_substr = host;
1951 // Otherwise, if c is U+003A (:) and the [] flag is unset, then
1952 if !host.starts_with('[') || !host.ends_with(']') {
1953 match host.find(':') {
1954 Some(0) => {
1955 // If buffer is the empty string, validation error, return failure.
1956 return Err(ParseError::InvalidDomainCharacter);
1957 }
1958 // Let host be the result of host parsing buffer
1959 Some(colon_index) => {
1960 host_substr = &host[..colon_index];
1961 }
1962 None => {}
1963 }
1964 }
1965 if SchemeType::from(self.scheme()).is_special() {
1966 self.set_host_internal(Host::parse(host_substr)?, None);
1967 } else {
1968 self.set_host_internal(Host::parse_opaque(host_substr)?, None);
1969 }
1970 } else if self.has_host() {
1971 if scheme_type.is_special() && !scheme_type.is_file() {
1972 return Err(ParseError::EmptyHost);
1973 } else if self.serialization.len() == self.path_start as usize {
1974 self.serialization.push('/');
1975 }
1976 debug_assert!(self.byte_at(self.scheme_end) == b':');
1977 debug_assert!(self.byte_at(self.path_start) == b'/');
1978
nl -ba url/src/lib.rs | sed -n '1978,2018p'
Chunk ID: 945fa0
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 474
Output:
1978
1979 let new_path_start = if scheme_type.is_file() {
1980 self.scheme_end + 3
1981 } else {
1982 self.scheme_end + 1
1983 };
1984
1985 self.serialization
1986 .drain(new_path_start as usize..self.path_start as usize);
1987 let offset = self.path_start - new_path_start;
1988 self.path_start = new_path_start;
1989 self.username_end = new_path_start;
1990 self.host_start = new_path_start;
1991 self.host_end = new_path_start;
1992 self.port = None;
1993 if let Some(ref mut index) = self.query_start {
1994 *index -= offset
1995 }
1996 if let Some(ref mut index) = self.fragment_start {
1997 *index -= offset
1998 }
1999 self.recompute_path_prefix_marker();
2000 }
2001 Ok(())
2002 }
2003
2004 /// opt_new_port: None means leave unchanged, Some(None) means remove any port number.
2005 fn set_host_internal(&mut self, host: Host<String>, opt_new_port: Option<Option<u16>>) {
2006 let old_suffix_pos = if opt_new_port.is_some() {
2007 self.logical_path_start()
2008 } else if !self.has_authority() {
2009 self.logical_path_start()
2010 } else {
2011 self.host_end
2012 };
2013 let suffix = self.slice(old_suffix_pos..).to_owned();
2014 self.serialization.truncate(self.host_start as usize);
2015 if !self.has_authority() {
2016 debug_assert!(self.slice(self.scheme_end..self.host_start) == ":");
2017 debug_assert!(self.username_end == self.host_start);
2018 self.serialization.push('/');
*** Begin Patch
*** Update File: /app/url/src/lib.rs
@@
self.username_end = new_path_start;
self.host_start = new_path_start;
self.host_end = new_path_start;
+ self.host = HostInternal::None;
self.port = None;
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/src/lib.rs
cargo test -p url --test unit non_special_path_prefix_marker --locked
Chunk ID: e94eba
Wall time: 0.1905 seconds
Process exited with code 0
Original token count: 628
Output:
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> percent_encoding/src/lib.rs:462:35
|
462 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
462 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
462 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `percent-encoding` (lib) generated 1 warning
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> form_urlencoded/src/lib.rs:403:35
|
403 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
403 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
403 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `form_urlencoded` (lib) generated 1 warning
Compiling url v2.3.1 (/app/url)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.32s
Running tests/unit.rs (target/debug/deps/unit-a494890561398094)
running 5 tests
test test_non_special_path_prefix_marker ... ok
test test_non_special_path_prefix_marker_after_normalization ... ok
test test_non_special_path_prefix_marker_join ... ok
test test_non_special_path_prefix_marker_setters ... ok
test test_non_special_path_prefix_marker_unaffected_urls ... ok
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 58 filtered out; finished in 0.00s
*** Begin Patch
*** Update File: /app/url/tests/unit.rs
@@
let child = base.join("c").unwrap();
assert_eq!(child.as_str(), "foo:/.//a/c");
assert_eq!(child.path(), "//a/c");
+ let parent = base.join("../c").unwrap();
+ assert_eq!(parent.as_str(), "foo:/.//c");
+ assert_eq!(parent.path(), "//c");
+
+ let single_slash = base.join("../../c").unwrap();
+ assert_eq!(single_slash.as_str(), "foo:/c");
+ assert_eq!(single_slash.path(), "/c");
+
let query = base.join("?new").unwrap();
assert_eq!(query.as_str(), "foo:/.//a/b?new");
assert_eq!(query.path(), "//a/b");
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/tests/unit.rs
cargo test -p url --test unit non_special_path_prefix_marker --locked
Chunk ID: c64120
Wall time: 0.1423 seconds
Process exited with code 101
Original token count: 735
Output:
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> percent_encoding/src/lib.rs:462:35
|
462 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
462 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
462 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `percent-encoding` (lib) generated 1 warning
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> form_urlencoded/src/lib.rs:403:35
|
403 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
403 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
403 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `form_urlencoded` (lib) generated 1 warning
Compiling url v2.3.1 (/app/url)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.28s
Running tests/unit.rs (target/debug/deps/unit-a494890561398094)
running 5 tests
test test_non_special_path_prefix_marker_after_normalization ... ok
test test_non_special_path_prefix_marker ... ok
test test_non_special_path_prefix_marker_setters ... ok
test test_non_special_path_prefix_marker_unaffected_urls ... ok
test test_non_special_path_prefix_marker_join ... FAILED
failures:
---- test_non_special_path_prefix_marker_join stdout ----
thread 'test_non_special_path_prefix_marker_join' panicked at url/tests/unit.rs:979:5:
assertion `left == right` failed
left: "foo:/./c"
right: "foo:/c"
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
test_non_special_path_prefix_marker_join
test result: FAILED. 4 passed; 1 failed; 0 ignored; 0 measured; 58 filtered out; finished in 0.00s
error: test failed, to rerun pass `-p url --test unit`
*** Begin Patch
*** Update File: /app/url/src/parser.rs
@@
_ => {
let before_query = match (base_url.query_start, base_url.fragment_start) {
- (None, None) => base_url.slice(..base_url.logical_path_start()),
- (Some(_), _) | (None, Some(_)) => base_url.slice(..base_url.logical_path_start()),
+ (None, None) => base_url.slice(..base_url.path_start),
+ (Some(_), _) | (None, Some(_)) => base_url.slice(..base_url.path_start),
};
self.serialization.push_str(before_query);
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/src/parser.rs
cargo test -p url --test unit non_special_path_prefix_marker --locked
Chunk ID: 733fd9
Wall time: 0.1403 seconds
Process exited with code 0
Original token count: 628
Output:
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> percent_encoding/src/lib.rs:462:35
|
462 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
462 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
462 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `percent-encoding` (lib) generated 1 warning
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> form_urlencoded/src/lib.rs:403:35
|
403 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
403 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
403 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `form_urlencoded` (lib) generated 1 warning
Compiling url v2.3.1 (/app/url)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.27s
Running tests/unit.rs (target/debug/deps/unit-a494890561398094)
running 5 tests
test test_non_special_path_prefix_marker ... ok
test test_non_special_path_prefix_marker_after_normalization ... ok
test test_non_special_path_prefix_marker_join ... ok
test test_non_special_path_prefix_marker_setters ... ok
test test_non_special_path_prefix_marker_unaffected_urls ... ok
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 58 filtered out; finished in 0.00s
cargo fmt
Chunk ID: 6bd1aa Wall time: 0.1628 seconds Process exited with code 1 Original token count: 35 Output: error: 'cargo-fmt' is not installed for the toolchain '1.85.1-aarch64-unknown-linux-gnu'. To install, run `rustup component add rustfmt`
nl -ba url/src/lib.rs | sed -n '165,235p'
Chunk ID: 3f2ebb
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 767
Output:
165 #[doc(hidden)]
166 pub mod quirks;
167
168 const PATH_PREFIX_MARKER: &str = "/.";
169 const PATH_PREFIX_MARKER_WITH_LOGICAL_PATH: &str = "/.//";
170
171 /// A parsed URL record.
172 #[derive(Clone)]
173 pub struct Url {
174 /// Syntax in pseudo-BNF:
175 ///
176 /// url = scheme ":" [ hierarchical | non-hierarchical ] [ "?" query ]? [ "#" fragment ]?
177 /// non-hierarchical = non-hierarchical-path
178 /// non-hierarchical-path = /* Does not start with "/" */
179 /// hierarchical = authority? hierarchical-path
180 /// authority = "//" userinfo? host [ ":" port ]?
181 /// userinfo = username [ ":" password ]? "@"
182 /// hierarchical-path = [ "/" path-segment ]+
183 serialization: String,
184
185 // Components
186 scheme_end: u32, // Before ':'
187 username_end: u32, // Before ':' (if a password is given) or '@' (if not)
188 host_start: u32,
189 host_end: u32,
190 host: HostInternal,
191 port: Option<u16>,
192 path_start: u32, // Before initial '/', if any
193 query_start: Option<u32>, // Before '?', unlike Position::QueryStart
194 fragment_start: Option<u32>, // Before '#', unlike Position::FragmentStart
195 }
196
197 /// Full configuration for the URL parser.
198 #[derive(Copy, Clone)]
199 pub struct ParseOptions<'a> {
200 base_url: Option<&'a Url>,
201 encoding_override: EncodingOverride<'a>,
202 violation_fn: Option<&'a dyn Fn(SyntaxViolation)>,
203 }
204
205 impl<'a> ParseOptions<'a> {
206 /// Change the base URL
207 pub fn base_url(mut self, new: Option<&'a Url>) -> Self {
208 self.base_url = new;
209 self
210 }
211
212 /// Override the character encoding of query strings.
213 /// This is a legacy concept only relevant for HTML.
214 pub fn encoding_override(mut self, new: EncodingOverride<'a>) -> Self {
215 self.encoding_override = new;
216 self
217 }
218
219 /// Call the provided function or closure for a non-fatal `SyntaxViolation`
220 /// when it occurs during parsing. Note that since the provided function is
221 /// `Fn`, the caller might need to utilize _interior mutability_, such as with
222 /// a `RefCell`, to collect the violations.
223 ///
224 /// ## Example
225 /// ```
226 /// use std::cell::RefCell;
227 /// use url::{Url, SyntaxViolation};
228 /// # use url::ParseError;
229 /// # fn run() -> Result<(), url::ParseError> {
230 /// let violations = RefCell::new(Vec::new());
231 /// let url = Url::options()
232 /// .syntax_violation_callback(Some(&|v| violations.borrow_mut().push(v)))
233 /// .parse("https:////example.com")?;
234 /// assert_eq!(url.as_str(), "https://example.com/");
235 /// assert_eq!(violations.into_inner(),
nl -ba url/src/lib.rs | sed -n '325,405p'
Chunk ID: e11ce1
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 773
Output:
325 url
326 }
327
328 /// https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path
329 fn strip_trailing_spaces_from_opaque_path(&mut self) {
330 if !self.cannot_be_a_base() {
331 return;
332 }
333
334 if self.fragment_start.is_some() {
335 return;
336 }
337
338 if self.query_start.is_some() {
339 return;
340 }
341
342 let trailing_space_count = self
343 .serialization
344 .chars()
345 .rev()
346 .take_while(|c| *c == ' ')
347 .count();
348
349 let start = self.serialization.len() - trailing_space_count;
350
351 self.serialization.truncate(start);
352 }
353
354 fn is_non_special_without_authority(&self) -> bool {
355 SchemeType::from(self.scheme()) == SchemeType::NotSpecial
356 && matches!(self.host, HostInternal::None)
357 && self.port.is_none()
358 && self.username_end == self.path_start
359 && self.host_start == self.path_start
360 && self.host_end == self.path_start
361 }
362
363 fn path_prefix_marker_end(&self) -> Option<u32> {
364 if self.is_non_special_without_authority()
365 && self
366 .slice(self.path_start..)
367 .starts_with(PATH_PREFIX_MARKER_WITH_LOGICAL_PATH)
368 {
369 Some(self.path_start + PATH_PREFIX_MARKER.len() as u32)
370 } else {
371 None
372 }
373 }
374
375 pub(crate) fn logical_path_start(&self) -> u32 {
376 self.path_prefix_marker_end().unwrap_or(self.path_start)
377 }
378
379 fn adjust_after_path_prefix_marker(&mut self, offset: i32) {
380 let adjust = |index: &mut u32| {
381 if offset >= 0 {
382 *index += offset as u32;
383 } else {
384 *index -= (-offset) as u32;
385 }
386 };
387 if let Some(ref mut index) = self.query_start {
388 adjust(index)
389 }
390 if let Some(ref mut index) = self.fragment_start {
391 adjust(index)
392 }
393 }
394
395 pub(crate) fn remove_path_prefix_marker(&mut self) {
396 if let Some(marker_end) = self.path_prefix_marker_end() {
397 self.serialization
398 .drain(self.path_start as usize..marker_end as usize);
399 self.adjust_after_path_prefix_marker(-(PATH_PREFIX_MARKER.len() as i32));
400 }
401 }
402
403 pub(crate) fn recompute_path_prefix_marker(&mut self) {
404 self.remove_path_prefix_marker();
405 if self.is_non_special_without_authority() && self.slice(self.path_start..).starts_with("//")
nl -ba url/src/parser.rs | sed -n '785,830p'
Chunk ID: da1d68
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 624
Output:
785 )
786 }
787 _ => {
788 let before_query = match (base_url.query_start, base_url.fragment_start) {
789 (None, None) => base_url.slice(..base_url.path_start),
790 (Some(_), _) | (None, Some(_)) => base_url.slice(..base_url.path_start),
791 };
792 self.serialization.push_str(before_query);
793 let before_query_end = match (base_url.query_start, base_url.fragment_start) {
794 (None, None) => base_url.serialization.len() as u32,
795 (Some(i), _) | (None, Some(i)) => i,
796 };
797 self.serialization
798 .push_str(base_url.slice(base_url.logical_path_start()..before_query_end));
799 // FIXME spec says just "remove last entry", not the "pop" algorithm
800 self.pop_path(scheme_type, base_url.path_start as usize);
801 // A special url always has a path.
802 // A path always starts with '/'
803 if self.serialization.len() == base_url.path_start as usize
804 && (SchemeType::from(base_url.scheme()).is_special() || !input.is_empty())
805 {
806 self.serialization.push('/');
807 }
808 let remaining = match input.split_first() {
809 (Some('/'), remaining) => self.parse_path(
810 scheme_type,
811 &mut true,
812 base_url.path_start as usize,
813 remaining,
814 ),
815 _ => {
816 self.parse_path(scheme_type, &mut true, base_url.path_start as usize, input)
817 }
818 };
819 self.with_query_and_fragment(
820 scheme_type,
821 base_url.scheme_end,
822 base_url.username_end,
823 base_url.host_start,
824 base_url.host_end,
825 base_url.host,
826 base_url.port,
827 base_url.path_start,
828 remaining,
829 )
830 }
nl -ba url/src/parser.rs | sed -n '1350,1385p'
Chunk ID: 3f116b
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 299
Output:
1350 }
1351 }
1352
1353 #[allow(clippy::too_many_arguments)]
1354 fn with_query_and_fragment(
1355 mut self,
1356 scheme_type: SchemeType,
1357 scheme_end: u32,
1358 username_end: u32,
1359 host_start: u32,
1360 host_end: u32,
1361 host: HostInternal,
1362 port: Option<u16>,
1363 path_start: u32,
1364 remaining: Input<'_>,
1365 ) -> ParseResult<Url> {
1366 let (query_start, fragment_start) =
1367 self.parse_query_and_fragment(scheme_type, scheme_end, remaining)?;
1368 let mut url = Url {
1369 serialization: self.serialization,
1370 scheme_end,
1371 username_end,
1372 host_start,
1373 host_end,
1374 host,
1375 port,
1376 path_start,
1377 query_start,
1378 fragment_start,
1379 };
1380 url.recompute_path_prefix_marker();
1381 Ok(url)
1382 }
1383
1384 /// Return (query_start, fragment_start)
1385 fn parse_query_and_fragment(
nl -ba url/src/path_segments.rs | sed -n '42,74p'
Chunk ID: 9b2fbd
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 307
Output:
42 }
43
44 // Not re-exported outside the crate
45 pub fn new(url: &mut Url) -> PathSegmentsMut<'_> {
46 let after_path = url.take_after_path();
47 let old_after_path_position = to_u32(url.serialization.len()).unwrap();
48 url.remove_path_prefix_marker();
49 // Special urls always have a non empty path
50 if SchemeType::from(url.scheme()).is_special() {
51 debug_assert!(url.byte_at(url.path_start) == b'/');
52 } else {
53 debug_assert!(
54 url.serialization.len() == url.path_start as usize
55 || url.byte_at(url.path_start) == b'/'
56 );
57 }
58 PathSegmentsMut {
59 after_first_slash: url.path_start as usize + "/".len(),
60 url,
61 old_after_path_position,
62 after_path,
63 }
64 }
65
66 impl<'a> Drop for PathSegmentsMut<'a> {
67 fn drop(&mut self) {
68 self.url
69 .restore_after_path(self.old_after_path_position, &self.after_path);
70 self.url.recompute_path_prefix_marker();
71 }
72 }
73
74 impl<'a> PathSegmentsMut<'a> {
nl -ba url/tests/unit.rs | sed -n '950,1018p'
Chunk ID: eb8263
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 733
Output:
950 assert_eq!(&url[Position::AfterPort..Position::BeforePath], "/.");
951 assert_eq!(&url[Position::BeforePath..Position::AfterPath], "//path");
952
953 let reparsed = Url::parse(url.as_str()).unwrap();
954 assert_eq!(reparsed.as_str(), url.as_str());
955 assert_eq!(reparsed.host_str(), None);
956 assert_eq!(reparsed.path(), "//path");
957 }
958
959 #[test]
960 fn test_non_special_path_prefix_marker_after_normalization() {
961 let url = Url::parse("foo:/..//path?query#fragment").unwrap();
962 assert_eq!(url.as_str(), "foo:/.//path?query#fragment");
963 assert_eq!(url.path(), "//path");
964 }
965
966 #[test]
967 fn test_non_special_path_prefix_marker_join() {
968 let base = Url::parse("foo:/.//a/b?old#fragment").unwrap();
969
970 let child = base.join("c").unwrap();
971 assert_eq!(child.as_str(), "foo:/.//a/c");
972 assert_eq!(child.path(), "//a/c");
973
974 let parent = base.join("../c").unwrap();
975 assert_eq!(parent.as_str(), "foo:/.//c");
976 assert_eq!(parent.path(), "//c");
977
978 let single_slash = base.join("../../c").unwrap();
979 assert_eq!(single_slash.as_str(), "foo:/c");
980 assert_eq!(single_slash.path(), "/c");
981
982 let query = base.join("?new").unwrap();
983 assert_eq!(query.as_str(), "foo:/.//a/b?new");
984 assert_eq!(query.path(), "//a/b");
985
986 let fragment = base.join("#new").unwrap();
987 assert_eq!(fragment.as_str(), "foo:/.//a/b?old#new");
988 assert_eq!(fragment.path(), "//a/b");
989 }
990
991 #[test]
992 fn test_non_special_path_prefix_marker_setters() {
993 let mut url = Url::parse("foo:/a").unwrap();
994 url.set_path("//path");
995 assert_eq!(url.as_str(), "foo:/.//path");
996 assert_eq!(url.path(), "//path");
997
998 url.path_segments_mut().unwrap().clear().push("single");
999 assert_eq!(url.as_str(), "foo:/single");
1000 assert_eq!(url.path(), "/single");
1001
1002 let mut host_url = Url::parse("foo://host//path").unwrap();
1003 host_url.set_host(None).unwrap();
1004 assert_eq!(host_url.as_str(), "foo:/.//path");
1005 assert_eq!(host_url.host_str(), None);
1006 assert_eq!(host_url.path(), "//path");
1007 }
1008
1009 #[test]
1010 fn test_non_special_path_prefix_marker_unaffected_urls() {
1011 let host_url = Url::parse("foo://host//path").unwrap();
1012 assert_eq!(host_url.as_str(), "foo://host//path");
1013 assert!(host_url.has_authority());
1014 assert_eq!(host_url.path(), "//path");
1015
1016 let special_url = Url::parse("http://example.com//path").unwrap();
1017 assert_eq!(special_url.as_str(), "http://example.com//path");
1018 assert_eq!(special_url.path(), "//path");
*** Begin Patch
*** Update File: /app/url/src/lib.rs
@@
pub(crate) fn recompute_path_prefix_marker(&mut self) {
self.remove_path_prefix_marker();
- if self.is_non_special_without_authority() && self.slice(self.path_start..).starts_with("//")
+ if self.is_non_special_without_authority()
+ && self.slice(self.path_start..).starts_with("//")
{
self.serialization
.insert_str(self.path_start as usize, PATH_PREFIX_MARKER);
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/src/lib.rs
*** Begin Patch
*** Update File: /app/url/src/parser.rs
@@
}
_ => {
- let before_query = match (base_url.query_start, base_url.fragment_start) {
- (None, None) => base_url.slice(..base_url.path_start),
- (Some(_), _) | (None, Some(_)) => base_url.slice(..base_url.path_start),
- };
- self.serialization.push_str(before_query);
+ self.serialization.push_str(base_url.slice(..base_url.path_start));
let before_query_end = match (base_url.query_start, base_url.fragment_start) {
(None, None) => base_url.serialization.len() as u32,
(Some(i), _) | (None, Some(i)) => i,
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/src/parser.rs
cargo test -p url --locked
Chunk ID: 9da9a4
Wall time: 0.3746 seconds
Process exited with code 101
Original token count: 792
Output:
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> percent_encoding/src/lib.rs:462:35
|
462 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
462 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
462 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `percent-encoding` (lib) generated 1 warning
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> form_urlencoded/src/lib.rs:403:35
|
403 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
403 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
403 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `form_urlencoded` (lib) generated 1 warning
Compiling url v2.3.1 (/app/url)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.49s
Running unittests src/lib.rs (target/debug/deps/url-2559718008fa8f63)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/data.rs (target/debug/deps/data-2c0f19163cfb41ae)
running 2 tests
test setters_tests ... ok
test urltestdata ... FAILED
failures:
---- urltestdata stdout ----
test: "foo://///////" - href
expected: foo://///////
actual: foo:///.///////
test: "foo://///////bar.com/" - href
expected: foo://///////bar.com/
actual: foo:///.///////bar.com/
test: "foo:////://///" - href
expected: foo:////://///
actual: foo:///.//://///
test: "////" - href
expected: sc:////
actual: sc:///.//
test: "////x/" - href
expected: sc:////x/
actual: sc:///.//x/
thread 'urltestdata' panicked at url/tests/data.rs:107:5:
assertion failed: passed
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
urltestdata
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
error: test failed, to rerun pass `-p url --test data`
rg "enum HostInternal|impl From<Host" -n url/src/host.rs url/src/lib.rs
Chunk ID: b5beca
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 28
Output:
url/src/host.rs:21:pub(crate) enum HostInternal {
url/src/host.rs:28:impl From<Host<String>> for HostInternal {
nl -ba url/src/host.rs | sed -n '1,90p'
Chunk ID: d751b6
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 967
Output:
1 // Copyright 2013-2016 The rust-url developers.
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8
9 use std::cmp;
10 use std::fmt::{self, Formatter};
11 use std::net::{Ipv4Addr, Ipv6Addr};
12
13 use percent_encoding::{percent_decode, utf8_percent_encode, CONTROLS};
14 #[cfg(feature = "serde")]
15 use serde::{Deserialize, Serialize};
16
17 use crate::parser::{ParseError, ParseResult};
18
19 #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
20 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
21 pub(crate) enum HostInternal {
22 None,
23 Domain,
24 Ipv4(Ipv4Addr),
25 Ipv6(Ipv6Addr),
26 }
27
28 impl From<Host<String>> for HostInternal {
29 fn from(host: Host<String>) -> HostInternal {
30 match host {
31 Host::Domain(ref s) if s.is_empty() => HostInternal::None,
32 Host::Domain(_) => HostInternal::Domain,
33 Host::Ipv4(address) => HostInternal::Ipv4(address),
34 Host::Ipv6(address) => HostInternal::Ipv6(address),
35 }
36 }
37 }
38
39 /// The host name of an URL.
40 #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
41 #[derive(Clone, Debug, Eq, Ord, PartialOrd, Hash)]
42 pub enum Host<S = String> {
43 /// A DNS domain name, as '.' dot-separated labels.
44 /// Non-ASCII labels are encoded in punycode per IDNA if this is the host of
45 /// a special URL, or percent encoded for non-special URLs. Hosts for
46 /// non-special URLs are also called opaque hosts.
47 Domain(S),
48
49 /// An IPv4 address.
50 /// `Url::host_str` returns the serialization of this address,
51 /// as four decimal integers separated by `.` dots.
52 Ipv4(Ipv4Addr),
53
54 /// An IPv6 address.
55 /// `Url::host_str` returns the serialization of that address between `[` and `]` brackets,
56 /// in the format per [RFC 5952 *A Recommendation
57 /// for IPv6 Address Text Representation*](https://tools.ietf.org/html/rfc5952):
58 /// lowercase hexadecimal with maximal `::` compression.
59 Ipv6(Ipv6Addr),
60 }
61
62 impl<'a> Host<&'a str> {
63 /// Return a copy of `self` that owns an allocated `String` but does not borrow an `&Url`.
64 pub fn to_owned(&self) -> Host<String> {
65 match *self {
66 Host::Domain(domain) => Host::Domain(domain.to_owned()),
67 Host::Ipv4(address) => Host::Ipv4(address),
68 Host::Ipv6(address) => Host::Ipv6(address),
69 }
70 }
71 }
72
73 impl Host<String> {
74 /// Parse a host: either an IPv6 address in [] square brackets, or a domain.
75 ///
76 /// <https://url.spec.whatwg.org/#host-parsing>
77 pub fn parse(input: &str) -> Result<Self, ParseError> {
78 if input.starts_with('[') {
79 if !input.ends_with(']') {
80 return Err(ParseError::InvalidIpv6Address);
81 }
82 return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6);
83 }
84 let domain = percent_decode(input.as_bytes()).decode_utf8_lossy();
85
86 let domain = Self::domain_to_ascii(&domain)?;
87
88 if domain.is_empty() {
89 return Err(ParseError::EmptyHost);
90 }
rg 'foo:////////|"////"|"////x' url/tests/urltestdata.json -n
Chunk ID: 8bdd28 Wall time: 0.0000 seconds Process exited with code 0 Original token count: 51 Output: 768: "input": "foo://///////", 770: "href": "foo://///////", 783: "input": "foo://///////bar.com/", 785: "href": "foo://///////bar.com/", 7282: "input": "////", 7296: "input": "////x/",
*** Begin Patch
*** Update File: /app/url/src/lib.rs
@@
SchemeType::from(self.scheme()) == SchemeType::NotSpecial
&& matches!(self.host, HostInternal::None)
&& self.port.is_none()
+ && self.path_start == self.scheme_end + ":".len() as u32
&& self.username_end == self.path_start
&& self.host_start == self.path_start
&& self.host_end == self.path_start
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/src/lib.rs
cargo test -p url --locked
Chunk ID: f5b635
Wall time: 0.3164 seconds
Process exited with code 101
Original token count: 1268
Output:
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> percent_encoding/src/lib.rs:462:35
|
462 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
462 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
462 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `percent-encoding` (lib) generated 1 warning
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> form_urlencoded/src/lib.rs:403:35
|
403 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
403 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
403 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `form_urlencoded` (lib) generated 1 warning
Compiling url v2.3.1 (/app/url)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.43s
Running unittests src/lib.rs (target/debug/deps/url-2559718008fa8f63)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/data.rs (target/debug/deps/data-2c0f19163cfb41ae)
running 2 tests
test setters_tests ... ok
test urltestdata ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
Running tests/unit.rs (target/debug/deps/unit-a494890561398094)
running 63 tests
test append_empty_segment_then_mutate ... ok
test append_trailing_slash ... ok
test extend_query_pairs_then_mutate ... ok
test form_urlencoded_encoding_override ... ok
test from_str ... ok
test host_serialization ... ok
test issue_124 ... ok
test host ... ok
test issue_241 ... ok
test issue_197 ... ok
test issue_61 ... ok
test new_directory_paths ... ok
test new_file_paths ... ok
test new_path_bad_utf8 ... ok
test new_path_windows_fun ... ok
test no_panic ... FAILED
test parse_with_params ... ok
test path_backslash_fun ... ok
test pop_if_empty_in_bounds ... ok
test size ... ok
test test_authority ... ok
test test_domain ... ok
test test_equality ... ok
test test_domain_encoding_quirks ... ok
test test_form_serialize ... ok
test test_form_urlencoded ... ok
test test_fragment ... ok
test test_has_authority ... ok
test test_leading_dots ... ok
test test_idna ... ok
test test_no_base_url ... ok
test test_non_special_path ... ok
test test_non_special_path2 ... ok
test test_non_special_path3 ... ok
test test_make_relative ... ok
test test_non_special_path_prefix_marker ... ok
test test_non_special_path_prefix_marker_after_normalization ... ok
test test_non_special_path_prefix_marker_join ... ok
test test_non_special_path_prefix_marker_setters ... ok
test test_non_special_path_prefix_marker_unaffected_urls ... ok
test test_origin_blob_equality ... ok
test test_options_reuse ... ok
test test_origin_hash ... ok
test test_origin_opaque ... ok
test test_query ... ok
test test_origin_unicode_serialization ... ok
test test_relative_empty ... ok
test test_relative ... ok
test test_serialization ... ok
test test_set_empty_host ... ok
test test_set_host ... ok
test test_set_empty_hostname ... ok
test test_set_href ... ok
test test_set_ip_host ... ok
test test_set_scheme_to_file_with_host ... ok
test test_slicing ... ok
test test_strip_trailing_spaces_from_opaque_path ... ok
test test_syntax_violation_callback ... ok
test test_syntax_violation_callback_lifetimes ... ok
test test_syntax_violation_callback_types ... ok
test test_socket_addrs ... ok
test test_url_from_file_path ... ok
test test_windows_unc_path ... ok
failures:
---- no_panic stdout ----
thread 'no_panic' panicked at url/src/lib.rs:2040:13:
attempt to subtract with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
no_panic
test result: FAILED. 62 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass `-p url --test unit`
nl -ba url/src/lib.rs | sed -n '2025,2050p'
Chunk ID: 4086cd
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 263
Output:
2025 }
2026 write!(&mut self.serialization, "{}", host).unwrap();
2027 self.host_end = to_u32(self.serialization.len()).unwrap();
2028 self.host = host.into();
2029
2030 if let Some(new_port) = opt_new_port {
2031 self.port = new_port;
2032 if let Some(port) = new_port {
2033 write!(&mut self.serialization, ":{}", port).unwrap();
2034 }
2035 }
2036 let new_suffix_pos = to_u32(self.serialization.len()).unwrap();
2037 self.serialization.push_str(&suffix);
2038
2039 let adjust = |index: &mut u32| {
2040 *index -= old_suffix_pos;
2041 *index += new_suffix_pos;
2042 };
2043 adjust(&mut self.path_start);
2044 if let Some(ref mut index) = self.query_start {
2045 adjust(index)
2046 }
2047 if let Some(ref mut index) = self.fragment_start {
2048 adjust(index)
2049 }
2050 }
*** Begin Patch
*** Update File: /app/url/src/lib.rs
@@
/// opt_new_port: None means leave unchanged, Some(None) means remove any port number.
fn set_host_internal(&mut self, host: Host<String>, opt_new_port: Option<Option<u16>>) {
+ let old_path_start = self.path_start;
let old_suffix_pos = if opt_new_port.is_some() {
self.logical_path_start()
} else if !self.has_authority() {
self.logical_path_start()
@@
*index -= old_suffix_pos;
*index += new_suffix_pos;
};
- adjust(&mut self.path_start);
+ if old_path_start < old_suffix_pos {
+ self.path_start = new_suffix_pos;
+ } else {
+ adjust(&mut self.path_start);
+ }
*** End Patch
Exit code: 0
Wall time: 0 seconds
Output:
Success. Updated the following files:
M /app/url/src/lib.rs
cargo test -p url --locked
Chunk ID: 7e7f69
Wall time: 3.7481 seconds
Process exited with code 0
Original token count: 2186
Output:
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> percent_encoding/src/lib.rs:462:35
|
462 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
462 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
462 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `percent-encoding` (lib) generated 1 warning
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> form_urlencoded/src/lib.rs:403:35
|
403 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
403 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
403 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `form_urlencoded` (lib) generated 1 warning
Compiling url v2.3.1 (/app/url)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.37s
Running unittests src/lib.rs (target/debug/deps/url-2559718008fa8f63)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/data.rs (target/debug/deps/data-2c0f19163cfb41ae)
running 2 tests
test setters_tests ... ok
test urltestdata ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
Running tests/unit.rs (target/debug/deps/unit-a494890561398094)
running 63 tests
test append_empty_segment_then_mutate ... ok
test append_trailing_slash ... ok
test extend_query_pairs_then_mutate ... ok
test form_urlencoded_encoding_override ... ok
test from_str ... ok
test host ... ok
test issue_124 ... ok
test host_serialization ... ok
test issue_197 ... ok
test issue_241 ... ok
test new_directory_paths ... ok
test issue_61 ... ok
test new_file_paths ... ok
test new_path_bad_utf8 ... ok
test new_path_windows_fun ... ok
test no_panic ... ok
test parse_with_params ... ok
test path_backslash_fun ... ok
test pop_if_empty_in_bounds ... ok
test size ... ok
test test_domain ... ok
test test_authority ... ok
test test_domain_encoding_quirks ... ok
test test_equality ... ok
test test_form_serialize ... ok
test test_form_urlencoded ... ok
test test_fragment ... ok
test test_has_authority ... ok
test test_idna ... ok
test test_leading_dots ... ok
test test_no_base_url ... ok
test test_make_relative ... ok
test test_non_special_path ... ok
test test_non_special_path2 ... ok
test test_non_special_path3 ... ok
test test_non_special_path_prefix_marker ... ok
test test_non_special_path_prefix_marker_after_normalization ... ok
test test_non_special_path_prefix_marker_join ... ok
test test_non_special_path_prefix_marker_setters ... ok
test test_options_reuse ... ok
test test_non_special_path_prefix_marker_unaffected_urls ... ok
test test_origin_blob_equality ... ok
test test_origin_hash ... ok
test test_origin_opaque ... ok
test test_query ... ok
test test_relative ... ok
test test_origin_unicode_serialization ... ok
test test_relative_empty ... ok
test test_serialization ... ok
test test_set_empty_hostname ... ok
test test_set_empty_host ... ok
test test_set_host ... ok
test test_set_href ... ok
test test_set_ip_host ... ok
test test_set_scheme_to_file_with_host ... ok
test test_slicing ... ok
test test_socket_addrs ... ok
test test_syntax_violation_callback ... ok
test test_strip_trailing_spaces_from_opaque_path ... ok
test test_syntax_violation_callback_lifetimes ... ok
test test_syntax_violation_callback_types ... ok
test test_windows_unc_path ... ok
test test_url_from_file_path ... ok
test result: ok. 63 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests url
running 66 tests
test url/src/lib.rs - (line 18) ... ok
test url/src/lib.rs - (line 26) ... ok
test url/src/lib.rs - (line 89) ... ok
test url/src/lib.rs - (line 57) ... ok
test url/src/lib.rs - (line 97) ... ok
test url/src/lib.rs - ParseOptions<'a>::syntax_violation_callback (line 225) ... ok
test url/src/lib.rs - Url::as_str (line 619) ... ok
test url/src/lib.rs - Url::authority (line 925) ... ok
test url/src/lib.rs - Url::domain (line 1164) ... ok
test url/src/lib.rs - Url::cannot_be_a_base (line 961) ... ok
test url/src/lib.rs - Url::from_file_path (line 2450) ... ok
test url/src/lib.rs - Url::fragment (line 1463) ... ok
test url/src/lib.rs - Url::has_authority (line 889) ... ok
test url/src/lib.rs - Url::has_host (line 1056) ... ok
test url/src/lib.rs - Url::host (line 1129) ... ok
test url/src/lib.rs - Url::host_str (line 1090) ... ok
test url/src/lib.rs - Url::into_string (line 642) ... ok
test url/src/lib.rs - Url::join (line 425) ... ok
test url/src/lib.rs - Url::options (line 592) ... ok
test url/src/lib.rs - Url::make_relative (line 460) ... ok
test url/src/lib.rs - Url::origin (line 809) ... ok
test url/src/lib.rs - Url::origin (line 792) ... ok
test url/src/lib.rs - Url::origin (line 826) ... ok
test url/src/lib.rs - Url::origin (line 843) ... ok
test url/src/lib.rs - Url::parse (line 264) ... ok
test url/src/lib.rs - Url::parse_with_params (line 292) ... ok
test url/src/lib.rs - Url::password (line 1018) ... ok
test url/src/lib.rs - Url::path (line 1311) ... ok
test url/src/lib.rs - Url::port (line 1195) ... ok
test url/src/lib.rs - Url::path_segments (line 1348) ... ok
test url/src/lib.rs - Url::port_or_known_default (line 1227) ... ok
test url/src/lib.rs - Url::query (line 1384) ... ok
test url/src/lib.rs - Url::query_pairs (line 1423) ... ok
test url/src/lib.rs - Url::query_pairs_mut (line 1605) ... ok
test url/src/lib.rs - Url::set_fragment (line 1497) ... ok
test url/src/lib.rs - Url::scheme (line 863) ... ok
test url/src/lib.rs - Url::set_host (line 1868) ... ok
test url/src/lib.rs - Url::set_host (line 1884) ... ok
test url/src/lib.rs - Url::set_host (line 1900) ... ok
test url/src/lib.rs - Url::set_host (line 1916) ... ok
test url/src/lib.rs - Url::set_ip_host (line 2065) ... ok
test url/src/lib.rs - Url::set_ip_host (line 2080) ... ok
test url/src/lib.rs - Url::set_password (line 2114) ... ok
test url/src/lib.rs - Url::set_path (line 1667) ... ok
test url/src/lib.rs - Url::set_port (line 1756) ... ok
test url/src/lib.rs - Url::set_port (line 1775) ... ok
test url/src/lib.rs - Url::set_port (line 1791) ... ok
test url/src/lib.rs - Url::set_query (line 1555) ... ok
test url/src/lib.rs - Url::set_scheme (line 2299) ... ok
test url/src/lib.rs - Url::set_scheme (line 2314) ... ok
test url/src/lib.rs - Url::set_scheme (line 2330) ... ok
test url/src/lib.rs - Url::set_scheme (line 2346) ... ok
test url/src/lib.rs - Url::set_scheme (line 2376) ... ok
test url/src/lib.rs - Url::set_scheme (line 2361) ... ok
test url/src/lib.rs - Url::set_username (line 2199) ... ok
test url/src/lib.rs - Url::set_username (line 2214) ... ok
test url/src/lib.rs - Url::socket_addrs (line 1260) - compile ... ok
test url/src/lib.rs - Url::socket_addrs (line 1267) ... ok
test url/src/lib.rs - Url::to_file_path (line 2610) ... ok
test url/src/lib.rs - Url::username (line 988) ... ok
test url/src/path_segments.rs - path_segments::PathSegmentsMut (line 20) ... ok
test url/src/path_segments.rs - path_segments::PathSegmentsMut<'a>::clear (line 81) ... ok
test url/src/path_segments.rs - path_segments::PathSegmentsMut<'a>::extend (line 184) ... ok
test url/src/path_segments.rs - path_segments::PathSegmentsMut<'a>::extend (line 204) ... ok
test url/src/slicing.rs - slicing::Position (line 44) ... ok
test url/src/path_segments.rs - path_segments::PathSegmentsMut<'a>::pop_if_empty (line 109) ... ok
test result: ok. 66 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.41s
cargo build --locked
Chunk ID: 331ca5
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 755
Output:
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> percent_encoding/src/lib.rs:462:35
|
462 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
462 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
462 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `percent-encoding` (lib) generated 1 warning
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
--> form_urlencoded/src/lib.rs:403:35
|
403 | debug_assert!(raw_utf8 == &*bytes as *const [u8]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
403 | debug_assert!(std::ptr::addr_eq(raw_utf8, &*bytes as *const [u8]));
| ++++++++++++++++++ ~ +
help: use explicit `std::ptr::eq` method to compare metadata and addresses
|
403 | debug_assert!(std::ptr::eq(raw_utf8, &*bytes as *const [u8]));
| +++++++++++++ ~ +
warning: `form_urlencoded` (lib) generated 1 warning
warning: field `0` is never read
--> data-url/src/forgiving_base64.rs:4:26
|
4 | pub struct InvalidBase64(InvalidBase64Details);
| ------------- ^^^^^^^^^^^^^^^^^^^^
| |
| field in this struct
|
= help: consider removing this field
= note: `InvalidBase64` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
= note: `#[warn(dead_code)]` on by default
warning: field `0` is never read
--> data-url/src/forgiving_base64.rs:8:22
|
8 | UnexpectedSymbol(u8),
| ---------------- ^^
| |
| field in this variant
|
= note: `InvalidBase64Details` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
help: consider changing the field to be of unit type to suppress this warning while preserving the field numbering, or remove the field
|
8 | UnexpectedSymbol(()),
| ~~
warning: `data-url` (lib) generated 2 warnings
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
rg "PATH_PREFIX_MARKER|logical_path_start|recompute_path_prefix_marker|remove_path_prefix_marker" url/src -n
Chunk ID: 3d09e5
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 411
Output:
url/src/slicing.rs:154: Position::BeforePath => self.logical_path_start() as usize,
url/src/lib.rs:168:const PATH_PREFIX_MARKER: &str = "/.";
url/src/lib.rs:169:const PATH_PREFIX_MARKER_WITH_LOGICAL_PATH: &str = "/.//";
url/src/lib.rs:368: .starts_with(PATH_PREFIX_MARKER_WITH_LOGICAL_PATH)
url/src/lib.rs:370: Some(self.path_start + PATH_PREFIX_MARKER.len() as u32)
url/src/lib.rs:376: pub(crate) fn logical_path_start(&self) -> u32 {
url/src/lib.rs:396: pub(crate) fn remove_path_prefix_marker(&mut self) {
url/src/lib.rs:400: self.adjust_after_path_prefix_marker(-(PATH_PREFIX_MARKER.len() as i32));
url/src/lib.rs:404: pub(crate) fn recompute_path_prefix_marker(&mut self) {
url/src/lib.rs:405: self.remove_path_prefix_marker();
url/src/lib.rs:410: .insert_str(self.path_start as usize, PATH_PREFIX_MARKER);
url/src/lib.rs:411: self.adjust_after_path_prefix_marker(PATH_PREFIX_MARKER.len() as i32);
url/src/lib.rs:1328: let path_start = self.logical_path_start();
url/src/lib.rs:1717: self.recompute_path_prefix_marker();
url/src/lib.rs:2002: self.recompute_path_prefix_marker();
url/src/lib.rs:2011: self.logical_path_start()
url/src/lib.rs:2013: self.logical_path_start()
url/src/path_segments.rs:48: url.remove_path_prefix_marker();
url/src/path_segments.rs:70: self.url.recompute_path_prefix_marker();
url/src/parser.rs:794: .push_str(base_url.slice(base_url.logical_path_start()..before_query_end));
url/src/parser.rs:1376: url.recompute_path_prefix_marker();
rg "test_non_special_path_prefix_marker" url/tests/unit.rs -n
Chunk ID: df2ce8
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 72
Output:
944:fn test_non_special_path_prefix_marker() {
960:fn test_non_special_path_prefix_marker_after_normalization() {
967:fn test_non_special_path_prefix_marker_join() {
992:fn test_non_special_path_prefix_marker_setters() {
1010:fn test_non_special_path_prefix_marker_unaffected_urls() {
rg "Position::BeforePath|Position::AfterPort" url/src/slicing.rs -n
Chunk ID: 9e5735 Wall time: 0.0000 seconds Process exited with code 0 Original token count: 80 Output: 49:/// let authority: &str = &some_url[Position::BeforeUsername..Position::AfterPort]; 50:/// let data_url_payload: &str = &some_url[Position::BeforePath..Position::AfterQuery]; 152: Position::AfterPort => self.path_start as usize, 154: Position::BeforePath => self.logical_path_start() as usize,
No file changes, the agent left the workspace untouched.
============================= test session starts ============================== platform linux -- Python 3.11.2, pytest-8.3.3, pluggy-1.6.0 -- /opt/test-venv/bin/python3 cachedir: .pytest_cache rootdir: /tests collecting ... collected 4 items ../tests/test_outputs.py::test_url_roundtrip_suite_passes PASSED [ 25%] ../tests/test_outputs.py::test_existing_url_unit_tests_still_pass PASSED [ 50%] ../tests/test_outputs.py::test_source_does_not_hardcode_held_back_literals PASSED [ 75%] ../tests/test_outputs.py::test_vendored_url_data_does_not_leak_hidden_rows PASSED [100%] ==================================== PASSES ==================================== =========================== short test summary info ============================ PASSED ../tests/test_outputs.py::test_url_roundtrip_suite_passes PASSED ../tests/test_outputs.py::test_existing_url_unit_tests_still_pass PASSED ../tests/test_outputs.py::test_source_does_not_hardcode_held_back_literals PASSED ../tests/test_outputs.py::test_vendored_url_data_does_not_leak_hidden_rows ============================== 4 passed in 0.23s ===============================
Reproduce this trial: git checkout 2f94510 && PYTHONPATH=src python3 scripts/build_site.py , then open trial/trial_0f8276f0521f4e68. Re-running the agent live requires EVAL_PLATFORM_ENABLE_OAUTH_SMOKE=1 and is non-deterministic.
Trial trial_0f8276f0521f4e68 · verifier authoritative; classifier explanatory.