proptest/arbitrary/_std/path.rs
1//-
2// Copyright 2017, 2018 The proptest developers
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! Arbitrary implementations for `std::path`.
11
12use std::path::*;
13
14use crate::{
15 arbitrary::{SMapped, StrategyFor},
16 path::PathParams,
17 prelude::{any, any_with, Arbitrary, Strategy},
18 std_facade::{string::ToString, Arc, Box, Rc, String, Vec},
19 strategy::{statics::static_map, MapInto},
20};
21
22arbitrary!(StripPrefixError; Path::new("").strip_prefix("a").unwrap_err());
23
24/// A private type (not actually pub) representing the output of [`PathParams`] that can't be
25/// referred to by API users.
26///
27/// The goal of this type is to encapsulate the output of `PathParams`. If this layer weren't
28/// present, the type of `<PathBuf as Arbitrary>::Strategy` would be `SMapped<(bool, Vec<String>),
29/// Self>`. This is a problem because it exposes the internal representation of `PathParams` as an
30/// API. For example, if an additional parameter of randomness (e.g. another bool) were added, the
31/// type of `Strategy` would change.
32///
33/// With `PathParamsOutput`, the type of `Strategy` is `SMapped<PathParamsOutput, Self>`, which is a
34/// type that can't be named directly---only via `<PathBuf as Arbitrary>::Strategy`. The internal
35/// representation of `PathParams` can be changed without affecting the API.
36#[derive(Debug)]
37pub struct PathParamsOutput {
38 is_absolute: bool,
39 components: Vec<String>,
40}
41
42impl Arbitrary for PathParamsOutput {
43 type Parameters = PathParams;
44 type Strategy = SMapped<(bool, Vec<String>), Self>;
45
46 fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
47 static_map(
48 (
49 any::<bool>(),
50 any_with::<Vec<String>>((
51 args.components(),
52 args.component_regex(),
53 )),
54 ),
55 |(is_absolute, components)| Self {
56 is_absolute,
57 components,
58 },
59 )
60 }
61}
62
63/// This implementation accepts as its argument a [`PathParams`] struct. It generates either a
64/// relative or an absolute path with equal probability.
65///
66/// Currently, this implementation does not generate:
67///
68/// * Paths that are not valid UTF-8 (this is unlikely to change)
69/// * Paths with a [`PrefixComponent`](std::path::PrefixComponent) on Windows, e.g. `C:\` (this may
70/// change in the future)
71impl Arbitrary for PathBuf {
72 type Parameters = PathParams;
73 type Strategy = SMapped<PathParamsOutput, Self>;
74
75 fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
76 static_map(
77 any_with::<PathParamsOutput>(args),
78 |PathParamsOutput {
79 is_absolute,
80 components,
81 }| {
82 let mut out = PathBuf::new();
83 if is_absolute {
84 out.push(&MAIN_SEPARATOR.to_string());
85 }
86
87 for component in components {
88 // If a component has an embedded / (or \ on Windows), remove it from the
89 // string.
90 let component = component
91 .chars()
92 .filter(|&c| !std::path::is_separator(c))
93 .collect::<String>();
94 out.push(&component);
95 }
96
97 out
98 },
99 )
100 }
101}
102
103macro_rules! dst_wrapped {
104 ($($w: ident),*) => {
105 $(
106 /// This implementation is identical to [the `Arbitrary` implementation for
107 /// `PathBuf`](trait.Arbitrary.html#impl-Arbitrary-for-PathBuf).
108 impl Arbitrary for $w<Path> {
109 type Parameters = PathParams;
110 type Strategy = MapInto<StrategyFor<PathBuf>, Self>;
111
112 fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
113 any_with::<PathBuf>(args).prop_map_into()
114 }
115 }
116 )*
117 }
118}
119
120dst_wrapped!(Box, Rc, Arc);
121
122#[cfg(test)]
123mod test {
124 no_panic_test!(
125 strip_prefix_error => StripPrefixError,
126 path_buf => PathBuf,
127 box_path => Box<Path>,
128 rc_path => Rc<Path>,
129 arc_path => Arc<Path>
130 );
131}