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