FunctionalPerl
view release on metacpan or search on metacpan
lib/FP/Path.pm view on Meta::CPAN
use FP::Show;
use FP::Equal;
use FP::Carp;
sub perhaps_segment_error {
@_ == 1 or fp_croak_arity 1;
my ($segment) = @_;
return "segments must be strings" unless is_string $segment;
return "segments cannot be the empty string" unless length $segment;
return "segment contains slash: " . show($segment) if $segment =~ m{/};
()
}
sub is_segment {
@_ == 1 or fp_croak_arity 1;
not perhaps_segment_error $_[0]
}
sub check_segment {
@_ == 1 or fp_croak_arity 1;
if (my ($e) = perhaps_segment_error $_[0]) {
die $e
}
}
# Toggle typing, off for speed (checking FP::List costs O(length);
# better use FP::StrictList if really interested in strict typing!)
sub use_costly_typing {
@_ == 0 or fp_croak_arity 0;
0
}
our $use_costly_typing = use_costly_typing; # for access from FP::Path::t
sub typed {
@_ == 2 or fp_croak_arity 2;
my ($pred, $name) = @_;
if (use_costly_typing) { [$pred, $name] }
else {
$name
}
}
use FP::Struct [
typed(list_of(\&is_segment), 'rsegments'), # reversed list
typed(\&is_boolean, 'has_endslash')
, # whether the path is forcibly specifying a
# dir by using a slash at the end (forcing a
# dir by ending in "." isn't setting this
# flag)
typed(\&is_boolean, 'is_absolute'), # bool
],
'FP::Struct::Show',
'FP::Abstract::Equal',
'FP::Abstract::Pure';
*import = constructorexporter new_from_string => "path";
sub new_from_string {
@_ == 2 or fp_croak_arity 2;
my ($class, $str) = @_;
my @p = split m{/+}, $str;
shift @p if (@p and $p[0] eq "");
$class->new(
array_to_list_reverse(\@p),
scalar $str =~ m{/$}s,
scalar $str =~ m{^/}s
)
}
sub FP_Equal_equal {
@_ == 2 or fp_croak_arity 2;
my ($a, $b) = @_;
# no need to compare is_absolute, since it is being distinguished
# anyway? Or better be safe than sorry?
( (!!$a->is_absolute eq !!$b->is_absolute)
and (!!$a->has_endslash eq !!$b->has_endslash)
and equal($a->rsegments, $b->rsegments))
}
sub segments {
my $s = shift;
$s->rsegments->reverse
}
sub string {
my $s = shift;
my $rs = $s->rsegments;
# check that no invalid segments have creeped in (by way of using
# the "lowlevel" accessors like segments_set, or the new or new_
# constructors directly; adding a type check to the segments field
# would solve this, but is less efficient as it would have to walk
# the list on every change instead of only stringification):
$rs->for_each(\&check_segment);
# force "." for empty relative paths:
my $rs1 = is_null($rs) && not($s->is_absolute) ? list(".") : $rs;
# add end slash
my $ss = ($s->has_endslash ? $rs1->cons("") : $rs1)->reverse;
# add start slash
($s->is_absolute ? $ss->cons("") : $ss)->strings_join("/")
}
# remove "." entries: (leave ".." in, because these cannot be resolved
# without reading the file system or knowing the usage)
sub clean_dot {
my $s = shift;
my $rseg = $s->rsegments;
$s->rsegments_set($rseg->filter(sub { not($_[0] eq ".") }))
->has_endslash_set(
# set forced dir flag if the last segment was a ".", even
# if previously it didn't end in "/"
$$s{has_endslash} or do {
if (is_null $rseg) {
0
} else {
$rseg->first eq "."
( run in 1.480 second using v1.01-cache-2.11-cpan-71847e10f99 )