blob: 2e71b03db8a34640401d90b9c98b1b3d636369ad [file] [log] [blame]
//-
// 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>
);
}