@@ -195,6 +195,15 @@ impl SourceId {
195
195
pub fn as_url ( & self ) -> SourceIdAsUrl < ' _ > {
196
196
SourceIdAsUrl {
197
197
inner : & * self . inner ,
198
+ encoded : false ,
199
+ }
200
+ }
201
+
202
+ /// Like [`Self::as_url`] but with URL parameters encoded.
203
+ pub fn as_encoded_url ( & self ) -> SourceIdAsUrl < ' _ > {
204
+ SourceIdAsUrl {
205
+ inner : & * self . inner ,
206
+ encoded : true ,
198
207
}
199
208
}
200
209
@@ -566,7 +575,7 @@ impl fmt::Display for SourceId {
566
575
// Don't replace the URL display for git references,
567
576
// because those are kind of expected to be URLs.
568
577
write ! ( f, "{}" , self . inner. url) ?;
569
- if let Some ( pretty) = reference. pretty_ref ( ) {
578
+ if let Some ( pretty) = reference. pretty_ref ( false ) {
570
579
write ! ( f, "?{}" , pretty) ?;
571
580
}
572
581
@@ -714,6 +723,7 @@ impl Ord for SourceKind {
714
723
/// A `Display`able view into a `SourceId` that will write it as a url
715
724
pub struct SourceIdAsUrl < ' a > {
716
725
inner : & ' a SourceIdInner ,
726
+ encoded : bool ,
717
727
}
718
728
719
729
impl < ' a > fmt:: Display for SourceIdAsUrl < ' a > {
@@ -731,7 +741,7 @@ impl<'a> fmt::Display for SourceIdAsUrl<'a> {
731
741
..
732
742
} => {
733
743
write ! ( f, "git+{}" , url) ?;
734
- if let Some ( pretty) = reference. pretty_ref ( ) {
744
+ if let Some ( pretty) = reference. pretty_ref ( self . encoded ) {
735
745
write ! ( f, "?{}" , pretty) ?;
736
746
}
737
747
if let Some ( precise) = precise. as_ref ( ) {
@@ -771,27 +781,49 @@ impl<'a> fmt::Display for SourceIdAsUrl<'a> {
771
781
impl GitReference {
772
782
/// Returns a `Display`able view of this git reference, or None if using
773
783
/// the head of the default branch
774
- pub fn pretty_ref ( & self ) -> Option < PrettyRef < ' _ > > {
784
+ pub fn pretty_ref ( & self , url_encoded : bool ) -> Option < PrettyRef < ' _ > > {
775
785
match self {
776
786
GitReference :: DefaultBranch => None ,
777
- _ => Some ( PrettyRef { inner : self } ) ,
787
+ _ => Some ( PrettyRef {
788
+ inner : self ,
789
+ url_encoded,
790
+ } ) ,
778
791
}
779
792
}
780
793
}
781
794
782
795
/// A git reference that can be `Display`ed
783
796
pub struct PrettyRef < ' a > {
784
797
inner : & ' a GitReference ,
798
+ url_encoded : bool ,
785
799
}
786
800
787
801
impl < ' a > fmt:: Display for PrettyRef < ' a > {
788
802
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
789
- match * self . inner {
790
- GitReference :: Branch ( ref b) => write ! ( f, "branch={}" , b) ,
791
- GitReference :: Tag ( ref s) => write ! ( f, "tag={}" , s) ,
792
- GitReference :: Rev ( ref s) => write ! ( f, "rev={}" , s) ,
803
+ let value: & str ;
804
+ match self . inner {
805
+ GitReference :: Branch ( s) => {
806
+ write ! ( f, "branch=" ) ?;
807
+ value = s;
808
+ }
809
+ GitReference :: Tag ( s) => {
810
+ write ! ( f, "tag=" ) ?;
811
+ value = s;
812
+ }
813
+ GitReference :: Rev ( s) => {
814
+ write ! ( f, "rev=" ) ?;
815
+ value = s;
816
+ }
793
817
GitReference :: DefaultBranch => unreachable ! ( ) ,
794
818
}
819
+ if self . url_encoded {
820
+ for value in url:: form_urlencoded:: byte_serialize ( value. as_bytes ( ) ) {
821
+ write ! ( f, "{value}" ) ?;
822
+ }
823
+ } else {
824
+ write ! ( f, "{value}" ) ?;
825
+ }
826
+ Ok ( ( ) )
795
827
}
796
828
}
797
829
@@ -905,6 +937,27 @@ mod tests {
905
937
assert_eq ! ( formatted, "sparse+https://my-crates.io/" ) ;
906
938
assert_eq ! ( source_id, deserialized) ;
907
939
}
940
+
941
+ #[ test]
942
+ fn gitrefs_roundtrip ( ) {
943
+ let base = "https://host/path" . into_url ( ) . unwrap ( ) ;
944
+ let branch = GitReference :: Branch ( "*-._+20%30 Z/z#foo=bar&zap[]?to\\ ()'\" " . to_string ( ) ) ;
945
+ let s1 = SourceId :: for_git ( & base, branch) . unwrap ( ) ;
946
+ let ser1 = format ! ( "{}" , s1. as_encoded_url( ) ) ;
947
+ let s2 = SourceId :: from_url ( & ser1) . expect ( "Failed to deserialize" ) ;
948
+ let ser2 = format ! ( "{}" , s2. as_encoded_url( ) ) ;
949
+ // Serializing twice should yield the same result
950
+ assert_eq ! ( ser1, ser2, "Serialized forms don't match" ) ;
951
+ // SourceId serializing the same should have the same semantics
952
+ // This used to not be the case (# was ambiguous)
953
+ assert_eq ! ( s1, s2, "SourceId doesn't round-trip" ) ;
954
+ // Freeze the format to match an x-www-form-urlencoded query string
955
+ // https://url.spec.whatwg.org/#application/x-www-form-urlencoded
956
+ assert_eq ! (
957
+ ser1,
958
+ "git+https://host/path?branch=*-._%2B20%2530+Z%2Fz%23foo%3Dbar%26zap%5B%5D%3Fto%5C%28%29%27%22"
959
+ ) ;
960
+ }
908
961
}
909
962
910
963
/// Check if `url` equals to the overridden crates.io URL.
0 commit comments