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}