Testing a small http-client on Linux using no_std & embedded reqwless.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Model xtask validation tasks with feature-owned command builders

Refs: reqwl-backend-feature-matrix
Refs: reqwl-dns-mode-features

rektide 4dbdf02b d21605c7

+203 -68
+203 -68
xtask/src/validate.rs
··· 5 5 6 6 const DNS_IP_ONLY_REJECT_NEEDLE: &str = "dns-ip-only mode accepts literal IP hosts only"; 7 7 const HTTP_EXAMPLE_URL: &str = "http://example.com"; 8 + const EMPTY_FEATURES: &[&str] = &[]; 9 + const HTTPS_FEATURES: &[&str] = &["https"]; 8 10 9 11 #[derive(Clone, Copy)] 10 12 enum BackendFeature { ··· 37 39 } 38 40 39 41 #[derive(Clone, Copy)] 40 - struct FeatureCombo { 41 - backend: BackendFeature, 42 - dns: DnsFeature, 42 + struct FeatureSelection { 43 + backend: Option<BackendFeature>, 44 + dns: Option<DnsFeature>, 45 + extra_features: &'static [&'static str], 46 + no_default_features: bool, 43 47 } 44 48 45 - impl FeatureCombo { 46 - const fn new(backend: BackendFeature, dns: DnsFeature) -> Self { 47 - Self { backend, dns } 49 + impl FeatureSelection { 50 + const fn none() -> Self { 51 + Self { 52 + backend: None, 53 + dns: None, 54 + extra_features: EMPTY_FEATURES, 55 + no_default_features: false, 56 + } 48 57 } 49 58 50 - fn feature_arg(self) -> String { 51 - format!("{},{}", self.backend.feature(), self.dns.feature()) 59 + const fn with_backend(self, backend: BackendFeature) -> Self { 60 + Self { 61 + backend: Some(backend), 62 + dns: self.dns, 63 + extra_features: self.extra_features, 64 + no_default_features: true, 65 + } 66 + } 67 + 68 + const fn with_dns(self, dns: DnsFeature) -> Self { 69 + Self { 70 + backend: self.backend, 71 + dns: Some(dns), 72 + extra_features: self.extra_features, 73 + no_default_features: true, 74 + } 75 + } 76 + 77 + const fn with_extras(self, extra_features: &'static [&'static str]) -> Self { 78 + Self { 79 + backend: self.backend, 80 + dns: self.dns, 81 + extra_features, 82 + no_default_features: self.no_default_features, 83 + } 84 + } 85 + 86 + fn feature_arg(self) -> Option<String> { 87 + let mut features = Vec::new(); 88 + if let Some(backend) = self.backend { 89 + features.push(backend.feature()); 90 + } 91 + if let Some(dns) = self.dns { 92 + features.push(dns.feature()); 93 + } 94 + features.extend(self.extra_features.iter().copied()); 95 + 96 + if features.is_empty() { 97 + return None; 98 + } 99 + Some(features.join(",")) 52 100 } 53 101 54 102 fn check_display(self) -> String { 55 - format!( 56 - "cargo check --no-default-features --features {}", 57 - self.feature_arg() 58 - ) 103 + match (self.no_default_features, self.feature_arg()) { 104 + (true, Some(features)) => { 105 + format!("cargo check --no-default-features --features {features}") 106 + } 107 + (false, Some(features)) => format!("cargo check --features {features}"), 108 + (_, None) => "cargo check".to_owned(), 109 + } 59 110 } 60 111 61 112 fn check_args(self) -> Vec<String> { 62 - let features = self.feature_arg(); 63 - vec![ 64 - "check".to_owned(), 65 - "--no-default-features".to_owned(), 66 - "--features".to_owned(), 67 - features, 68 - ] 113 + let mut args = vec!["check".to_owned()]; 114 + if let Some(features) = self.feature_arg() { 115 + if self.no_default_features { 116 + args.push("--no-default-features".to_owned()); 117 + } 118 + args.push("--features".to_owned()); 119 + args.push(features); 120 + } 121 + args 69 122 } 70 123 71 124 fn run_display(self, url: &str) -> String { 72 - format!( 73 - "cargo run --no-default-features --features {} -- {}", 74 - self.feature_arg(), 75 - url 76 - ) 125 + match (self.no_default_features, self.feature_arg()) { 126 + (true, Some(features)) => { 127 + format!("cargo run --no-default-features --features {features} -- {url}") 128 + } 129 + (false, Some(features)) => { 130 + format!("cargo run --features {features} -- {url}") 131 + } 132 + (_, None) => format!("cargo run -- {url}"), 133 + } 77 134 } 78 135 79 136 fn run_args(self, url: &str) -> Vec<String> { 80 - let features = self.feature_arg(); 81 - vec![ 82 - "run".to_owned(), 83 - "--no-default-features".to_owned(), 84 - "--features".to_owned(), 85 - features, 86 - "--".to_owned(), 87 - url.to_owned(), 88 - ] 137 + let mut args = vec!["run".to_owned()]; 138 + if let Some(features) = self.feature_arg() { 139 + if self.no_default_features { 140 + args.push("--no-default-features".to_owned()); 141 + } 142 + args.push("--features".to_owned()); 143 + args.push(features); 144 + } 145 + args.push("--".to_owned()); 146 + args.push(url.to_owned()); 147 + args 148 + } 149 + } 150 + 151 + trait FeatureCommand: Copy { 152 + fn default_selection(self) -> FeatureSelection; 153 + 154 + fn check_display(self) -> String { 155 + self.default_selection().check_display() 156 + } 157 + 158 + fn check_args(self) -> Vec<String> { 159 + self.default_selection().check_args() 160 + } 161 + 162 + fn run_display(self, url: &str) -> String { 163 + self.default_selection().run_display(url) 164 + } 165 + 166 + fn run_args(self, url: &str) -> Vec<String> { 167 + self.default_selection().run_args(url) 168 + } 169 + } 170 + 171 + impl FeatureCommand for BackendFeature { 172 + fn default_selection(self) -> FeatureSelection { 173 + FeatureSelection::none() 174 + .with_backend(self) 175 + .with_dns(DnsFeature::GetAddrInfo) 176 + } 177 + } 178 + 179 + impl FeatureCommand for DnsFeature { 180 + fn default_selection(self) -> FeatureSelection { 181 + FeatureSelection::none() 182 + .with_backend(BackendFeature::PosixLibc) 183 + .with_dns(self) 89 184 } 90 185 } 91 186 ··· 117 212 RunRustixIpOnlyReject, 118 213 } 119 214 215 + impl ValidateTask { 216 + fn selection(self) -> Option<FeatureSelection> { 217 + match self { 218 + Self::Check => Some(FeatureSelection::none()), 219 + Self::CheckHttps => Some(FeatureSelection::none().with_extras(HTTPS_FEATURES)), 220 + Self::CheckIpOnly => Some(DnsFeature::IpOnly.default_selection()), 221 + Self::RunHttpExample => Some(FeatureSelection::none()), 222 + Self::RunIpOnlyReject => Some(DnsFeature::IpOnly.default_selection()), 223 + Self::CheckRustix => Some(BackendFeature::Rustix.default_selection()), 224 + Self::CheckRustixIpOnly => Some( 225 + FeatureSelection::none() 226 + .with_backend(BackendFeature::Rustix) 227 + .with_dns(DnsFeature::IpOnly), 228 + ), 229 + Self::RunRustixHttpExample => Some(BackendFeature::Rustix.default_selection()), 230 + Self::RunRustixIpOnlyReject => Some( 231 + FeatureSelection::none() 232 + .with_backend(BackendFeature::Rustix) 233 + .with_dns(DnsFeature::IpOnly), 234 + ), 235 + Self::All | Self::Build | Self::Runtime => None, 236 + } 237 + } 238 + } 239 + 120 240 pub fn run_validate_tasks(tasks: &[ValidateTask]) -> Result<(), String> { 121 241 if tasks.is_empty() { 122 242 return run_validate_task(ValidateTask::All); ··· 125 245 } 126 246 127 247 fn run_validate_task(task: ValidateTask) -> Result<(), String> { 128 - let posix_ip_only = FeatureCombo::new(BackendFeature::PosixLibc, DnsFeature::IpOnly); 129 - let rustix_getaddrinfo = FeatureCombo::new(BackendFeature::Rustix, DnsFeature::GetAddrInfo); 130 - let rustix_ip_only = FeatureCombo::new(BackendFeature::Rustix, DnsFeature::IpOnly); 131 - 132 248 match task { 133 249 ValidateTask::All => run_sequence(&[ 134 250 ValidateTask::Check, ··· 154 270 ValidateTask::RunIpOnlyReject, 155 271 ValidateTask::RunRustixIpOnlyReject, 156 272 ]), 157 - ValidateTask::Check => run_success("cargo check", &cargo_args(&["check"])), 158 - ValidateTask::CheckHttps => run_success( 159 - "cargo check --features https", 160 - &cargo_args(&["check", "--features", "https"]), 161 - ), 162 - ValidateTask::CheckIpOnly => run_check_combo(posix_ip_only), 163 - ValidateTask::RunHttpExample => run_success( 164 - "cargo run -- http://example.com", 165 - &cargo_args(&["run", "--", HTTP_EXAMPLE_URL]), 166 - ), 167 - ValidateTask::RunIpOnlyReject => run_ip_only_reject_combo(posix_ip_only), 168 - ValidateTask::CheckRustix => run_check_combo(rustix_getaddrinfo), 169 - ValidateTask::CheckRustixIpOnly => run_check_combo(rustix_ip_only), 170 - ValidateTask::RunRustixHttpExample => run_http_example_combo(rustix_getaddrinfo), 171 - ValidateTask::RunRustixIpOnlyReject => run_ip_only_reject_combo(rustix_ip_only), 273 + ValidateTask::Check => run_check_task(task), 274 + ValidateTask::CheckHttps => run_check_task(task), 275 + ValidateTask::CheckIpOnly => run_check_dns_task(DnsFeature::IpOnly), 276 + ValidateTask::RunHttpExample => run_run_task(task, HTTP_EXAMPLE_URL), 277 + ValidateTask::RunIpOnlyReject => run_ip_only_reject_dns_task(DnsFeature::IpOnly), 278 + ValidateTask::CheckRustix => run_check_backend_task(BackendFeature::Rustix), 279 + ValidateTask::CheckRustixIpOnly => run_check_task(task), 280 + ValidateTask::RunRustixHttpExample => { 281 + run_run_backend_task(BackendFeature::Rustix, HTTP_EXAMPLE_URL) 282 + } 283 + ValidateTask::RunRustixIpOnlyReject => run_ip_only_reject_task(task), 172 284 } 173 285 } 174 286 175 - fn run_check_combo(combo: FeatureCombo) -> Result<(), String> { 176 - let display = combo.check_display(); 177 - let args = combo.check_args(); 178 - run_success(&display, &args) 287 + fn run_check_task(task: ValidateTask) -> Result<(), String> { 288 + let selection = task 289 + .selection() 290 + .ok_or_else(|| "task has no feature selection".to_owned())?; 291 + run_success(&selection.check_display(), &selection.check_args()) 292 + } 293 + 294 + fn run_run_task(task: ValidateTask, url: &str) -> Result<(), String> { 295 + let selection = task 296 + .selection() 297 + .ok_or_else(|| "task has no feature selection".to_owned())?; 298 + run_success(&selection.run_display(url), &selection.run_args(url)) 299 + } 300 + 301 + fn run_ip_only_reject_task(task: ValidateTask) -> Result<(), String> { 302 + let selection = task 303 + .selection() 304 + .ok_or_else(|| "task has no feature selection".to_owned())?; 305 + run_expected_failure( 306 + &selection.run_display(HTTP_EXAMPLE_URL), 307 + &selection.run_args(HTTP_EXAMPLE_URL), 308 + DNS_IP_ONLY_REJECT_NEEDLE, 309 + ) 179 310 } 180 311 181 - fn run_http_example_combo(combo: FeatureCombo) -> Result<(), String> { 182 - let display = combo.run_display(HTTP_EXAMPLE_URL); 183 - let args = combo.run_args(HTTP_EXAMPLE_URL); 184 - run_success(&display, &args) 312 + fn run_check_backend_task(backend: BackendFeature) -> Result<(), String> { 313 + run_success(&backend.check_display(), &backend.check_args()) 314 + } 315 + 316 + fn run_run_backend_task(backend: BackendFeature, url: &str) -> Result<(), String> { 317 + run_success(&backend.run_display(url), &backend.run_args(url)) 318 + } 319 + 320 + fn run_check_dns_task(dns: DnsFeature) -> Result<(), String> { 321 + run_success(&dns.check_display(), &dns.check_args()) 185 322 } 186 323 187 - fn run_ip_only_reject_combo(combo: FeatureCombo) -> Result<(), String> { 188 - let display = combo.run_display(HTTP_EXAMPLE_URL); 189 - let args = combo.run_args(HTTP_EXAMPLE_URL); 190 - run_expected_failure(&display, &args, DNS_IP_ONLY_REJECT_NEEDLE) 324 + fn run_ip_only_reject_dns_task(dns: DnsFeature) -> Result<(), String> { 325 + run_expected_failure( 326 + &dns.run_display(HTTP_EXAMPLE_URL), 327 + &dns.run_args(HTTP_EXAMPLE_URL), 328 + DNS_IP_ONLY_REJECT_NEEDLE, 329 + ) 191 330 } 192 331 193 332 fn run_sequence(tasks: &[ValidateTask]) -> Result<(), String> { ··· 235 374 .args(cargo_args) 236 375 .output() 237 376 .map_err(|error| format!("failed to execute cargo: {error}")) 238 - } 239 - 240 - fn cargo_args(parts: &[&str]) -> Vec<String> { 241 - parts.iter().map(|part| (*part).to_owned()).collect() 242 377 } 243 378 244 379 fn workspace_root() -> &'static Path {