proptest_http_message/request_line/target/
absolute_form.rs1use std::{num::NonZero, ops::RangeInclusive};
4
5use proptest::{option::of, prelude::Strategy};
6
7use crate::request_line::target::components::{
8 authority::{Authority, authority},
9 fragment::fragment,
10 path::{Path, path_absolute},
11 query::{QueryParam, query},
12 scheme::http_scheme,
13};
14
15#[derive(Debug)]
17pub struct AbsoluteForm {
18 pub scheme: String,
19 pub authority: Authority,
20 pub path: Option<Path>,
21 pub query: Option<Vec<QueryParam>>,
22 pub fragment: Option<String>,
23}
24
25pub fn absolute(
27 max_label_count: usize,
28 max_segments: NonZero<usize>,
29 query_count_range: RangeInclusive<usize>,
30) -> impl Strategy<Value = (AbsoluteForm, String)> {
31 (
32 http_scheme(),
33 authority(max_label_count),
34 of(path_absolute(max_segments)),
35 of(query(*query_count_range.start(), *query_count_range.end())),
36 of(fragment()),
37 )
38 .prop_map(|(scheme, (authority, authority_repr), path, query, fragment)| {
39 let repr = format!(
40 "{scheme}://{authority}{path}{query}{fragment}",
41 scheme = scheme,
42 authority = authority_repr,
43 path = if let Some(path) = path.as_ref() { path.1.as_str() } else { "" },
44 query = query.as_ref().map(|(_, query)| format!("?{query}")).as_deref().unwrap_or_default(),
45 fragment =
46 fragment.as_ref().map(|fragment| format!("#{fragment}")).as_deref().unwrap_or_default()
47 );
48
49 (
50 AbsoluteForm {
51 scheme,
52 authority,
53 path: path.map(|p| p.0),
54 query: query.map(|q| q.0),
55 fragment,
56 },
57 repr,
58 )
59 })
60}
61
62#[cfg(test)]
63pub(super) mod tests {
64 use claims::{assert_none, assert_ok};
65 use proptest::proptest;
66 use url::{Host, Url};
67
68 use super::*;
69
70 pub(in super::super) fn absolute_asserts(absolute_form: &AbsoluteForm, repr: &str) {
71 let url = assert_ok!(Url::parse(repr), "should be good URL but got {repr}");
72 assert_eq!(absolute_form.scheme.to_ascii_lowercase(), url.scheme().to_ascii_lowercase());
73 if let Some(user_info) = absolute_form.authority.user_info.as_ref() {
74 assert_eq!(
75 user_info.username,
76 url.username(),
77 "expected to get username {:?} but got {:?}",
78 user_info.username,
79 url.username()
80 );
81 assert_eq!(
82 user_info.password.as_deref(),
83 url.password(),
84 "expected to get password {:?} but got {:?}",
85 user_info.password,
86 url.password()
87 );
88 } else {
89 assert!(url.username().is_empty(), "username should be empty if user info is absent");
90 assert_none!(url.password(), "password should not exist if user info is absent");
91 }
92
93 match (&absolute_form.authority.host, url.host()) {
94 (
95 crate::request_line::target::components::host::Host::Domain(domain),
96 Some(Host::Domain(domain2)),
97 ) => assert_eq!(
98 domain.to_lowercase(),
99 domain2.to_lowercase(),
100 "expected domain {:?} but parsed domain {:?}",
101 domain.to_lowercase(),
102 domain2.to_lowercase()
103 ),
104 (
105 crate::request_line::target::components::host::Host::Ipv6(ipv6_addr, _),
106 Some(Host::Ipv6(ipv6_addr2)),
107 ) => assert_eq!(
108 *ipv6_addr, ipv6_addr2,
109 "expected IP v6 {ipv6_addr:?} but parsed IP v6 {ipv6_addr:?}"
110 ),
111 (
112 crate::request_line::target::components::host::Host::Ipv4(ipv4_addr, _),
113 Some(Host::Ipv4(ipv4_addr2)),
114 ) => assert_eq!(
115 *ipv4_addr, ipv4_addr2,
116 "expected IP v6 {ipv4_addr:?} but parsed IP v6 {ipv4_addr:?}"
117 ),
118 _ => panic!("expected host {:?} but parsed {:?}", absolute_form.authority.host, url.host()),
119 }
120
121 match (absolute_form.authority.port, url.port()) {
122 (None | Some(80), None) => {}
123 (Some(port), Some(port2)) => {
124 assert_eq!(port, port2, "expected port but {port} parsed {port2}");
125 }
126 (port, port2) => panic!("expected port but {port:?} parsed {port2:?}"),
127 }
128
129 match (&absolute_form.path, url.path()) {
130 (Some(path), path2) => {
131 assert_eq!(path.normalized, path2, "expected path {path:?} but parsed {path2}");
132 }
133 (None, path) => {
134 assert!(path.is_empty() || path == "/", "expected path to be empty but parsed {path}");
135 }
136 }
137
138 match (absolute_form.query.as_deref(), url.query()) {
139 (None, None) => {}
140 (Some(query), Some(query2)) => {
141 let query = query
142 .iter()
143 .map(|query| format!("{}={}", query.key, query.value.as_deref().unwrap_or_default()))
144 .collect::<Vec<_>>()
145 .join("&");
146 assert_eq!(query, query2, "expected query {query:?} but parsed {query2:?}");
147 }
148 (query, query2) => panic!("expected query {query:?} but got {query2:?}"),
149 }
150
151 match (absolute_form.fragment.as_deref(), url.fragment()) {
152 (None, None) => {}
153 (Some(fragment), Some(fragment2)) => {
154 assert_eq!(fragment, fragment2, "expected fragment {fragment:?} but parsed {fragment2:?}");
155 }
156 (fragment, fragment2) => panic!("expected fragment {fragment:?} but parsed {fragment2:?}"),
157 }
158 }
159 proptest! {
160 #[test]
161 fn absolute_works((absolute_form, repr) in absolute(20, 50.try_into().unwrap(), 0..=20)) {
162 absolute_asserts(&absolute_form, &repr);
163 }
164 }
165}