217 changes: 116 additions & 101 deletions std/string.d
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,17 @@ public import std.uni : icmp, toLower, toLowerInPlace, toUpper, toUpperInPlace;
public import std.format : format, sformat;
import std.typecons : Flag;

import std.meta;
import std.range.primitives;
import std.traits;
import std.meta; // AliasSeq, staticIndexOf
import std.range.primitives; // back, ElementEncodingType, ElementType, front,
// hasLength, hasSlicing, isBidirectionalRange, isForwardRange, isInfinite,
// isInputRange, isOutputRange, isRandomAccessRange, popBack, popFront, put,
// save;
import std.traits; // isConvertibleToString, isNarrowString, isSomeChar,
// isSomeString, StringTypeOf, Unqual

//public imports for backward compatibility
public import std.algorithm : startsWith, endsWith, cmp, count;
public import std.algorithm.comparison : cmp;
public import std.algorithm.searching : startsWith, endsWith, count;
public import std.array : join, replace, replaceInPlace, split, empty;

/* ************* Exceptions *************** */
Expand Down Expand Up @@ -580,10 +585,11 @@ unittest
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;
import std.traits : EnumMembers;
import std.utf : byChar, byWchar, byDchar;
debug(string) trustedPrintf("string.indexOf.unittest\n");

import std.exception;
import std.utf : byChar, byWchar, byDchar;
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
Expand Down Expand Up @@ -636,9 +642,10 @@ unittest
@safe pure unittest
{
import std.conv : to;
import std.traits : EnumMembers;
import std.utf : byCodeUnit, byChar, byWchar;
debug(string) trustedPrintf("string.indexOf(startIdx).unittest\n");

import std.utf : byCodeUnit, byChar, byWchar;
assert("hello".byCodeUnit.indexOf(cast(dchar)'l', 1) == 2);
assert("hello".byWchar.indexOf(cast(dchar)'l', 1) == 2);
assert("hello".byWchar.indexOf(cast(dchar)'l', 6) == -1);
Expand Down Expand Up @@ -710,12 +717,11 @@ ptrdiff_t indexOf(Range, Char)(Range s, const(Char)[] sub,
if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
isSomeChar!Char)
{
import std.uni : toLower;
alias Char1 = Unqual!(ElementEncodingType!Range);

static if (isSomeString!Range)
{
import std.algorithm : find;
import std.algorithm.searching : find;

const(Char1)[] balance;
if (cs == CaseSensitive.yes)
Expand Down Expand Up @@ -830,9 +836,10 @@ unittest
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;
import std.traits : EnumMembers;
debug(string) trustedPrintf("string.indexOf.unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
Expand Down Expand Up @@ -883,6 +890,7 @@ unittest
@safe pure @nogc nothrow
unittest
{
import std.traits : EnumMembers;
import std.utf : byWchar;

foreach (cs; EnumMembers!CaseSensitive)
Expand All @@ -900,6 +908,7 @@ unittest
@safe pure unittest
{
import std.conv : to;
import std.traits : EnumMembers;
debug(string) trustedPrintf("string.indexOf(startIdx).unittest\n");

foreach (S; AliasSeq!(string, wstring, dstring))
Expand Down Expand Up @@ -1071,9 +1080,10 @@ ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c, in size_t startIdx,
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;
import std.traits : EnumMembers;
debug(string) trustedPrintf("string.lastIndexOf.unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
Expand Down Expand Up @@ -1111,6 +1121,7 @@ ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c, in size_t startIdx,
@safe pure unittest
{
import std.conv : to;
import std.traits : EnumMembers;

debug(string) trustedPrintf("string.lastIndexOf.unittest\n");

Expand Down Expand Up @@ -1165,9 +1176,10 @@ ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub,
in CaseSensitive cs = CaseSensitive.yes) @safe pure
if (isSomeChar!Char1 && isSomeChar!Char2)
{
import std.utf : strideBack;
import std.algorithm.searching : endsWith;
import std.conv : to;
import std.algorithm : endsWith;
import std.range.primitives : walkLength;
import std.utf : strideBack;
if (sub.empty)
return -1;

Expand Down Expand Up @@ -1289,10 +1301,11 @@ ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub,
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;
import std.traits : EnumMembers;

debug(string) trustedPrintf("string.lastIndexOf.unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
Expand Down Expand Up @@ -1367,6 +1380,7 @@ ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub,
@safe pure unittest
{
import std.conv : to;
import std.traits : EnumMembers;

debug(string) trustedPrintf("string.lastIndexOf.unittest\n");

Expand Down Expand Up @@ -1419,14 +1433,13 @@ private ptrdiff_t indexOfAnyNeitherImpl(bool forward, bool any, Char, Char2)(
in CaseSensitive cs = CaseSensitive.yes) @safe pure
if (isSomeChar!Char && isSomeChar!Char2)
{
import std.algorithm : canFind;
import std.algorithm.searching : canFind, findAmong;
if (cs == CaseSensitive.yes)
{
static if (forward)
{
static if (any)
{
import std.algorithm : findAmong;
size_t n = haystack.findAmong(needles).length;
return n ? haystack.length - n : -1;
}
Expand All @@ -1445,9 +1458,8 @@ private ptrdiff_t indexOfAnyNeitherImpl(bool forward, bool any, Char, Char2)(
{
static if (any)
{
import std.utf : strideBack;
import std.algorithm : findAmong;
import std.range : retro;
import std.utf : strideBack;
size_t n = haystack.retro.findAmong(needles).source.length;
if (n)
{
Expand All @@ -1468,7 +1480,7 @@ private ptrdiff_t indexOfAnyNeitherImpl(bool forward, bool any, Char, Char2)(
}
else
{
import std.uni : toLower;
import std.range.primitives: walkLength;
if (needles.length <= 16 && needles.walkLength(17))
{
size_t si = 0;
Expand Down Expand Up @@ -1615,11 +1627,11 @@ ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles,

@safe pure unittest
{
import std.exception : assertCTFEable;
import std.conv : to;

debug(string) trustedPrintf("string.indexOfAny.unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
Expand Down Expand Up @@ -1657,6 +1669,7 @@ ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles,
@safe pure unittest
{
import std.conv : to;
import std.traits : EnumMembers;

debug(string) trustedPrintf("string.indexOfAny(startIdx).unittest\n");

Expand Down Expand Up @@ -1783,10 +1796,10 @@ ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack,
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.lastIndexOfAny.unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
Expand Down Expand Up @@ -1838,10 +1851,10 @@ ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack,
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.lastIndexOfAny(index).unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
Expand Down Expand Up @@ -1965,10 +1978,10 @@ ptrdiff_t indexOfNeither(Char,Char2)(const(Char)[] haystack,
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.indexOf.unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
Expand Down Expand Up @@ -2011,10 +2024,10 @@ ptrdiff_t indexOfNeither(Char,Char2)(const(Char)[] haystack,
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.indexOfNeither(index).unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
Expand Down Expand Up @@ -2123,10 +2136,10 @@ ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack,
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.lastIndexOfNeither.unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
Expand Down Expand Up @@ -2170,10 +2183,10 @@ ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack,
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.lastIndexOfNeither(index).unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring))
Expand Down Expand Up @@ -2227,6 +2240,7 @@ ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack,
auto representation(Char)(Char[] s) @safe pure nothrow @nogc
if (isSomeChar!Char)
{
import std.traits : ModifyTypePreservingTQ;
alias ToRepType(T) = AliasSeq!(ubyte, ushort, uint)[T.sizeof / 2];
return cast(ModifyTypePreservingTQ!(ToRepType, Char)[])s;
}
Expand All @@ -2242,8 +2256,9 @@ auto representation(Char)(Char[] s) @safe pure nothrow @nogc

@trusted pure unittest
{
import std.exception;
import std.typecons;
import std.exception : assertCTFEable;
import std.traits : Fields;
import std.typecons : Tuple;

assertCTFEable!(
{
Expand Down Expand Up @@ -2287,9 +2302,9 @@ auto representation(Char)(Char[] s) @safe pure nothrow @nogc
S capitalize(S)(S input) @trusted pure
if (isSomeString!S)
{
import std.uni : asCapitalized;
import std.array : array;
import std.conv : to;
import std.array;
import std.uni : asCapitalized;

return input.asCapitalized.array.to!S;
}
Expand All @@ -2314,10 +2329,10 @@ auto capitalize(S)(auto ref S s)

@safe pure unittest
{
import std.algorithm.comparison : cmp;
import std.conv : to;
import std.algorithm : cmp;
import std.exception : assertCTFEable;

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
Expand Down Expand Up @@ -2381,8 +2396,8 @@ alias KeepTerminator = Flag!"keepTerminator";
S[] splitLines(S)(S s, in KeepTerminator keepTerm = KeepTerminator.no) @safe pure
if (isSomeString!S)
{
import std.uni : lineSep, paraSep;
import std.array : appender;
import std.uni : lineSep, paraSep;

size_t iStart = 0;
auto retval = appender!(S[])();
Expand Down Expand Up @@ -2482,10 +2497,10 @@ unittest
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.splitLines.unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
Expand Down Expand Up @@ -2547,8 +2562,8 @@ unittest

private struct LineSplitter(KeepTerminator keepTerm = KeepTerminator.no, Range)
{
import std.uni : lineSep, paraSep;
import std.conv : unsigned;
import std.uni : lineSep, paraSep;
private:
Range _input;

Expand Down Expand Up @@ -2731,12 +2746,12 @@ auto lineSplitter(KeepTerminator keepTerm = KeepTerminator.no, Range)(auto ref R

@safe pure unittest
{
import std.conv : to;
import std.array : array;
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.lineSplitter.unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
Expand Down Expand Up @@ -2852,10 +2867,10 @@ auto stripLeft(Range)(Range input)
}
else
{
auto save = input.save;
auto inputSave = input.save;
auto dc = decodeFront(input);
if (!std.uni.isWhite(dc))
return save;
return inputSave;
}
}
return input;
Expand All @@ -2876,8 +2891,8 @@ auto stripLeft(Range)(Range input)
assert(stripLeft([paraSep] ~ "hello world" ~ paraSep) ==
"hello world" ~ [paraSep]);

import std.array : array;
import std.utf : byChar;
import std.array;
assert(stripLeft(" hello world "w.byChar).array ==
"hello world ");
}
Expand Down Expand Up @@ -3031,9 +3046,9 @@ unittest

unittest
{
import std.utf;
import std.array;
import std.array : array;
import std.uni : lineSep, paraSep;
import std.utf : byChar, byDchar, byUTF, byWchar, invalidUTFstrings;
assert(stripRight(" hello world ".byChar).array == " hello world");
assert(stripRight("\n\t\v\rhello world\n\t\v\r"w.byWchar).array == "\n\t\v\rhello world"w);
assert(stripRight("hello world"d.byDchar).array == "hello world"d);
Expand Down Expand Up @@ -3105,12 +3120,12 @@ auto strip(Range)(auto ref Range str)

@safe pure unittest
{
import std.algorithm.comparison : equal;
import std.conv : to;
import std.algorithm : equal;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.strip.unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!( char[], const char[], string,
Expand Down Expand Up @@ -3142,8 +3157,8 @@ auto strip(Range)(auto ref Range str)

@safe pure unittest
{
import std.exception;
import std.range;
import std.array : sameHead, sameTail;
import std.exception : assertCTFEable;
assertCTFEable!(
{
wstring s = " ";
Expand Down Expand Up @@ -3239,7 +3254,7 @@ Range chomp(Range, C2)(Range str, const(C2)[] delimiter)

static if (is(Unqual!C1 == Unqual!C2) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4)))
{
import std.algorithm : endsWith;
import std.algorithm.searching : endsWith;
if (str.endsWith(delimiter))
return str[0 .. $ - delimiter.length];
return str;
Expand Down Expand Up @@ -3269,8 +3284,8 @@ Range chomp(Range, C2)(Range str, const(C2)[] delimiter)
@safe pure
unittest
{
import std.utf : decode;
import std.uni : lineSep, paraSep, nelSep;
import std.utf : decode;
assert(chomp(" hello world \n\r") == " hello world \n");
assert(chomp(" hello world \r\n") == " hello world ");
assert(chomp(" hello world \f") == " hello world ");
Expand Down Expand Up @@ -3312,11 +3327,11 @@ unittest
unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.chomp.unittest\n");
string s;

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
Expand Down Expand Up @@ -3356,8 +3371,8 @@ unittest
});

// Ranges
import std.array : array;
import std.utf : byChar, byWchar, byDchar;
import std.array;
assert(chomp("hello world\r\n" .byChar ).array == "hello world");
assert(chomp("hello world\r\n"w.byWchar).array == "hello world"w);
assert(chomp("hello world\r\n"d.byDchar).array == "hello world"d);
Expand Down Expand Up @@ -3392,7 +3407,7 @@ Range chompPrefix(Range, C2)(Range str, const(C2)[] delimiter)

static if (is(Unqual!C1 == Unqual!C2) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4)))
{
import std.algorithm : startsWith;
import std.algorithm.searching : startsWith;
if (str.startsWith(delimiter))
return str[delimiter.length .. $];
return str;
Expand Down Expand Up @@ -3436,9 +3451,9 @@ StringTypeOf!Range chompPrefix(Range, C2)(auto ref Range str, const(C2)[] delimi
@safe pure
unittest
{
import std.algorithm.comparison : equal;
import std.conv : to;
import std.algorithm : equal;
import std.exception;
import std.exception : assertCTFEable;
assertCTFEable!(
{
foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
Expand All @@ -3455,8 +3470,8 @@ unittest
});

// Ranges
import std.array : array;
import std.utf : byChar, byWchar, byDchar;
import std.array;
assert(chompPrefix("hello world" .byChar , "hello"d).array == " world");
assert(chompPrefix("hello world"w.byWchar, "hello" ).array == " world"w);
assert(chompPrefix("hello world"d.byDchar, "hello"w).array == " world"d);
Expand Down Expand Up @@ -3567,8 +3582,8 @@ unittest

@safe pure unittest
{
import std.array : array;
import std.utf : byChar, byWchar, byDchar, byCodeUnit, invalidUTFstrings;
import std.array;

assert(chop("hello world".byChar).array == "hello worl");
assert(chop("hello world\n"w.byWchar).array == "hello world"w);
Expand Down Expand Up @@ -3601,12 +3616,12 @@ unittest

unittest
{
import std.algorithm.comparison : equal;
import std.conv : to;
import std.algorithm : equal;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.chop.unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
Expand Down Expand Up @@ -3642,7 +3657,7 @@ unittest
S leftJustify(S)(S s, size_t width, dchar fillChar = ' ')
if (isSomeString!S)
{
import std.array;
import std.array : array;
return leftJustifier(s, width, fillChar).array;
}

Expand Down Expand Up @@ -3743,7 +3758,7 @@ auto leftJustifier(Range)(Range r, size_t width, dchar fillChar = ' ')
@safe pure @nogc nothrow
unittest
{
import std.algorithm : equal;
import std.algorithm.comparison : equal;
import std.utf : byChar;
assert(leftJustifier("hello", 2).equal("hello".byChar));
assert(leftJustifier("hello", 7).equal("hello ".byChar));
Expand Down Expand Up @@ -3790,7 +3805,7 @@ unittest
S rightJustify(S)(S s, size_t width, dchar fillChar = ' ')
if (isSomeString!S)
{
import std.array;
import std.array : array;
return rightJustifier(s, width, fillChar).array;
}

Expand Down Expand Up @@ -3860,7 +3875,8 @@ auto rightJustifier(Range)(Range r, size_t width, dchar fillChar = ' ')
else
{
// Lookahead to see now many fill characters are needed
import std.range : walkLength, take;
import std.range : take;
import std.range.primitives : walkLength;
nfill = _width - walkLength(_input.save.take(_width), _width);
}
inited = true;
Expand Down Expand Up @@ -3921,7 +3937,7 @@ auto rightJustifier(Range)(Range r, size_t width, dchar fillChar = ' ')
@safe pure @nogc nothrow
unittest
{
import std.algorithm : equal;
import std.algorithm.comparison : equal;
import std.utf : byChar;
assert(rightJustifier("hello", 2).equal("hello".byChar));
assert(rightJustifier("hello", 7).equal(" hello".byChar));
Expand Down Expand Up @@ -3978,7 +3994,7 @@ unittest
S center(S)(S s, size_t width, dchar fillChar = ' ')
if (isSomeString!S)
{
import std.array;
import std.array : array;
return centerJustifier(s, width, fillChar).array;
}

Expand All @@ -3994,10 +4010,10 @@ S center(S)(S s, size_t width, dchar fillChar = ' ')
unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.justify.unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
Expand Down Expand Up @@ -4063,7 +4079,8 @@ auto centerJustifier(Range)(Range r, size_t width, dchar fillChar = ' ')
}
else static if (C.sizeof == 4)
{
import std.range : chain, repeat, walkLength;
import std.range : chain, repeat;
import std.range.primitives : walkLength;

auto len = walkLength(r.save, width);
if (len > width)
Expand All @@ -4080,7 +4097,7 @@ auto centerJustifier(Range)(Range r, size_t width, dchar fillChar = ' ')
@safe pure @nogc nothrow
unittest
{
import std.algorithm : equal;
import std.algorithm.comparison : equal;
import std.utf : byChar;
assert(centerJustifier("hello", 2).equal("hello".byChar));
assert(centerJustifier("hello", 8).equal(" hello ".byChar));
Expand Down Expand Up @@ -4152,15 +4169,13 @@ auto detab(Range)(auto ref Range s, size_t tabSize = 8) pure
if ((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range))
|| __traits(compiles, StringTypeOf!Range))
{
import std.array;
import std.array : array;
return detabber(s, tabSize).array;
}

///
@trusted pure unittest
{
import std.array;

assert(detab(" \n\tx", 9) == " \n x");
}

Expand Down Expand Up @@ -4314,7 +4329,7 @@ auto detabber(Range)(Range r, size_t tabSize = 8)
///
@trusted pure unittest
{
import std.array;
import std.array : array;

assert(detabber(" \n\tx", 9).array == " \n x");
}
Expand All @@ -4332,12 +4347,12 @@ unittest

@trusted pure unittest
{
import std.algorithm.comparison : cmp;
import std.conv : to;
import std.algorithm : cmp;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.detab.unittest\n");

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
Expand Down Expand Up @@ -4365,8 +4380,8 @@ unittest
///
@trusted pure unittest
{
import std.utf;
import std.array;
import std.array : array;
import std.utf : byChar, byWchar;

assert(detabber(" \u2029\t".byChar, 9).array == " \u2029 ");
auto r = "hel\tx".byWchar.detabber();
Expand Down Expand Up @@ -4654,7 +4669,7 @@ auto entabber(Range)(Range r, size_t tabSize = 8)
///
unittest
{
import std.array;
import std.array : array;
assert(entabber(" x \n").array == "\tx\n");
}

Expand All @@ -4673,10 +4688,10 @@ unittest
unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.entab.unittest\n");

import std.exception;
assertCTFEable!(
{
assert(entab(cast(string) null) is null);
Expand Down Expand Up @@ -4721,8 +4736,8 @@ unittest
@safe pure
unittest
{
import std.array : array;
import std.utf : byChar;
import std.array;
assert(entabber(" \u0085 aa".byChar).array == "\u0085 aa");
assert(entabber(" \u2028\t aa \t".byChar).array == "\u2028\t aa");

Expand Down Expand Up @@ -4790,8 +4805,8 @@ C1[] translate(C1, C2 = immutable char)(C1[] str,
@trusted pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!( char[], const( char)[], immutable( char)[],
Expand Down Expand Up @@ -4847,8 +4862,8 @@ C1[] translate(C1, S, C2 = immutable char)(C1[] str,
@trusted pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

import std.exception;
assertCTFEable!(
{
foreach (S; AliasSeq!( char[], const( char)[], immutable( char)[],
Expand Down Expand Up @@ -5110,8 +5125,8 @@ body
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

import std.exception;
assertCTFEable!(
{
foreach (C; AliasSeq!(char, const char, immutable char))
Expand Down Expand Up @@ -5249,10 +5264,10 @@ bool inPattern(S)(dchar c, in S pattern) @safe pure @nogc if (isSomeString!S)
@safe pure @nogc unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("std.string.inPattern.unittest\n");

import std.exception;
assertCTFEable!(
{
assert(inPattern('x', "x") == 1);
Expand Down Expand Up @@ -5312,10 +5327,10 @@ size_t countchars(S, S1)(S s, in S1 pattern) @safe pure @nogc if (isSomeString!S
@safe pure @nogc unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("std.string.count.unittest\n");

import std.exception;
assertCTFEable!(
{
assert(countchars("abc", "a-c") == 3);
Expand Down Expand Up @@ -5360,10 +5375,10 @@ S removechars(S)(S s, in S pattern) @safe pure if (isSomeString!S)
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("std.string.removechars.unittest\n");

import std.exception;
assertCTFEable!(
{
assert(removechars("abc", "a-c").length == 0);
Expand Down Expand Up @@ -5426,10 +5441,10 @@ S squeeze(S)(S s, in S pattern = null)
@trusted pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("std.string.squeeze.unittest\n");

import std.exception;
assertCTFEable!(
{
string s;
Expand Down Expand Up @@ -5558,10 +5573,10 @@ S succ(S)(S s) @safe pure if (isSomeString!S)
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("std.string.succ.unittest\n");

import std.exception;
assertCTFEable!(
{
assert(succ(string.init) is null);
Expand Down Expand Up @@ -5616,9 +5631,9 @@ S succ(S)(S s) @safe pure if (isSomeString!S)
C1[] tr(C1, C2, C3, C4 = immutable char)
(C1[] str, const(C2)[] from, const(C3)[] to, const(C4)[] modifiers = null)
{
import std.array : appender;
import std.conv : conv_to = to;
import std.utf : decode;
import std.array : appender;

bool mod_c;
bool mod_d;
Expand Down Expand Up @@ -5729,10 +5744,11 @@ C1[] tr(C1, C2, C3, C4 = immutable char)

unittest
{
import std.algorithm.comparison : equal;
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("std.string.tr.unittest\n");
import std.algorithm : equal;

// Complete list of test types; too slow to test'em all
// alias TestTypes = AliasSeq!(
Expand All @@ -5743,7 +5759,6 @@ unittest
// Reduced list of test types
alias TestTypes = AliasSeq!(char[], const(wchar)[], immutable(dchar)[]);

import std.exception;
assertCTFEable!(
{
foreach (S; TestTypes)
Expand Down Expand Up @@ -5998,7 +6013,6 @@ bool isNumeric(S)(S s, bool bAllowSep = false) if (isSomeString!S ||
// Test string types
@safe unittest
{
import std.meta : AliasSeq;
import std.conv : to;

foreach (T; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
Expand Down Expand Up @@ -6043,10 +6057,10 @@ unittest
@trusted unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("isNumeric(in string, bool = false).unittest\n");

import std.exception;
assertCTFEable!(
{
// Test the isNumeric(in string) function
Expand Down Expand Up @@ -6226,7 +6240,7 @@ body

@safe pure nothrow unittest
{
import std.exception;
import std.exception : assertCTFEable;
assertCTFEable!(
{
char[4] buffer;
Expand Down Expand Up @@ -6270,7 +6284,7 @@ body
assert(soundex("Hardin") == "H635");
assert(soundex("Martinez") == "M635");

import std.utf;
import std.utf : byChar, byDchar, byWchar;
assert(soundexer("Martinez".byChar ) == "M635");
assert(soundexer("Martinez".byWchar) == "M635");
assert(soundexer("Martinez".byDchar) == "M635");
Expand All @@ -6295,7 +6309,7 @@ unittest

string[string] abbrev(string[] values) @safe pure
{
import std.algorithm : sort;
import std.algorithm.sorting : sort;

string[string] result;

Expand Down Expand Up @@ -6348,20 +6362,20 @@ unittest
import std.string;

static string[] list = [ "food", "foxy" ];
auto abbrevs = std.string.abbrev(list);
auto abbrevs = abbrev(list);
assert(abbrevs == ["fox": "foxy", "food": "food",
"foxy": "foxy", "foo": "food"]);
}


@trusted pure unittest
{
import std.algorithm.sorting : sort;
import std.conv : to;
import std.algorithm : sort;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.abbrev.unittest\n");

import std.exception;
assertCTFEable!(
{
string[] values;
Expand Down Expand Up @@ -6501,10 +6515,10 @@ unittest
@safe @nogc unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.column.unittest\n");

import std.exception;
assertCTFEable!(
{
assert(column(string.init) == 0);
Expand Down Expand Up @@ -6615,10 +6629,10 @@ S wrap(S)(S s, in size_t columns = 80, S firstindent = null,
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.wrap.unittest\n");

import std.exception;
assertCTFEable!(
{
assert(wrap(string.init) == "\n");
Expand Down Expand Up @@ -6695,7 +6709,7 @@ void main() {
*/
S[] outdent(S)(S[] lines) @safe pure if (isSomeString!S)
{
import std.algorithm : startsWith;
import std.algorithm.searching : startsWith;

if (lines.empty)
{
Expand Down Expand Up @@ -6755,6 +6769,7 @@ S[] outdent(S)(S[] lines) @safe pure if (isSomeString!S)
@safe pure unittest
{
import std.conv : to;
import std.exception : assertCTFEable;

debug(string) trustedPrintf("string.outdent.unittest\n");

Expand Down Expand Up @@ -6782,7 +6797,6 @@ S[] outdent(S)(S[] lines) @safe pure if (isSomeString!S)
";
}

import std.exception;
assertCTFEable!(
{

Expand Down Expand Up @@ -6864,6 +6878,7 @@ See_Also: $(LREF representation)
auto assumeUTF(T)(T[] arr) pure
if (staticIndexOf!(Unqual!T, ubyte, ushort, uint) != -1)
{
import std.traits : ModifyTypePreservingTQ;
import std.utf : validate;
alias ToUTFType(U) = AliasSeq!(char, wchar, dchar)[U.sizeof / 2];
auto asUTF = cast(ModifyTypePreservingTQ!(ToUTFType, T)[])arr;
Expand All @@ -6883,7 +6898,7 @@ auto assumeUTF(T)(T[] arr) pure

pure unittest
{
import std.algorithm : equal;
import std.algorithm.comparison : equal;
foreach (T; AliasSeq!(char[], wchar[], dchar[]))
{
immutable T jti = "Hello World";
Expand Down
47 changes: 32 additions & 15 deletions std/traits.d
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
*/
module std.traits;

import std.typetuple;
import std.typetuple; // TypeTuple

///////////////////////////////////////////////////////////////////////////////
// Functions
Expand Down Expand Up @@ -334,7 +334,7 @@ private alias parentOf(alias sym : T!Args, alias T, Args...) = Identity!(__trait
*/
template packageName(alias T)
{
import std.algorithm : startsWith;
import std.algorithm.searching : startsWith;

static if (__traits(compiles, parentOf!T))
enum parent = packageName!(parentOf!T);
Expand Down Expand Up @@ -388,7 +388,7 @@ version (none) version(unittest) //Please uncomment me when changing packageName
*/
template moduleName(alias T)
{
import std.algorithm : startsWith;
import std.algorithm.searching : startsWith;

static assert(!T.stringof.startsWith("package "), "cannot get the module name for a package");

Expand Down Expand Up @@ -540,7 +540,7 @@ private template fqnSym(alias T)

static string adjustIdent(string s)
{
import std.algorithm : skipOver, findSplit;
import std.algorithm.searching : findSplit, skipOver;

if (s.skipOver("package ") || s.skipOver("module "))
return s;
Expand Down Expand Up @@ -623,8 +623,10 @@ private template fqnType(T,

static if (parameters.length)
{
import std.algorithm : map;
import std.range : join, zip;
import std.algorithm.iteration : map;
import std.array : join;
import std.meta : staticMap;
import std.range : zip;

string result = join(
map!(a => format("%s%s", a[0], a[1]))(
Expand Down Expand Up @@ -721,8 +723,6 @@ private template fqnType(T,
}
else static if (isStaticArray!T)
{
import std.conv;

enum fqnType = chain!(
format("%s[%s]", fqnType!(typeof(T.init[0]), qualifiers), T.length)
);
Expand Down Expand Up @@ -1908,7 +1908,7 @@ template SetFunctionAttributes(T, string linkage, uint attrs)
if (isFunctionPointer!T || isDelegate!T)
{
mixin({
import std.algorithm : canFind;
import std.algorithm.searching : canFind;

static assert(!(attrs & FunctionAttribute.trusted) ||
!(attrs & FunctionAttribute.safe),
Expand Down Expand Up @@ -2012,7 +2012,7 @@ version (unittest)
}
unittest
{
import std.algorithm : reduce;
import std.algorithm.iteration : reduce;

alias FA = FunctionAttribute;
foreach (BaseT; TypeTuple!(typeof(&sc), typeof(&novar), typeof(&cstyle),
Expand Down Expand Up @@ -2088,6 +2088,7 @@ have a context pointer.
*/
template hasNested(T)
{
import std.meta : anySatisfy;
static if (isStaticArray!T && T.length)
enum hasNested = hasNested!(typeof(T.init[0]));
else static if (is(T == class) || is(T == struct) || is(T == union))
Expand Down Expand Up @@ -2226,6 +2227,7 @@ private enum NameOf(alias T) = T.stringof;
*/
template FieldNameTuple(T)
{
import std.meta : staticMap;
static if (is(T == struct) || is(T == union))
alias FieldNameTuple = staticMap!(NameOf, T.tupleof[0 .. $ - isNested!T]);
else static if (is(T == class))
Expand Down Expand Up @@ -2686,6 +2688,7 @@ $(LI a delegate.))
*/
template hasAliasing(T...)
{
import std.meta : anySatisfy;
import std.typecons : Rebindable;

static if (T.length && is(T[0] : Rebindable!R, R))
Expand Down Expand Up @@ -2789,6 +2792,7 @@ $(LI an associative array.) $(LI a delegate.))
*/
template hasIndirections(T)
{
import std.meta : anySatisfy;
static if (is(T == struct) || is(T == union))
enum hasIndirections = anySatisfy!(.hasIndirections, FieldTypeTuple!T);
else static if (isStaticArray!T && is(T : E[N], E, size_t N))
Expand Down Expand Up @@ -2899,6 +2903,7 @@ immutable or shared.) $(LI a delegate that is not shared.))

template hasUnsharedAliasing(T...)
{
import std.meta : anySatisfy;
import std.typecons : Rebindable;

static if (!T.length)
Expand Down Expand Up @@ -3074,6 +3079,7 @@ unittest
*/
template hasElaborateCopyConstructor(S)
{
import std.meta : anySatisfy;
static if (isStaticArray!S && S.length)
{
enum bool hasElaborateCopyConstructor = hasElaborateCopyConstructor!(typeof(S.init[0]));
Expand Down Expand Up @@ -3130,6 +3136,7 @@ unittest
*/
template hasElaborateAssign(S)
{
import std.meta : anySatisfy;
static if (isStaticArray!S && S.length)
{
enum bool hasElaborateAssign = hasElaborateAssign!(typeof(S.init[0]));
Expand Down Expand Up @@ -3216,6 +3223,7 @@ unittest
*/
template hasElaborateDestructor(S)
{
import std.meta : anySatisfy;
static if (isStaticArray!S && S.length)
{
enum bool hasElaborateDestructor = hasElaborateDestructor!(typeof(S.init[0]));
Expand Down Expand Up @@ -3599,6 +3607,7 @@ unittest
*/
template InterfacesTuple(T)
{
import std.meta : NoDuplicates;
template Flatten(H, T...)
{
static if (T.length)
Expand Down Expand Up @@ -3735,6 +3744,7 @@ template MemberFunctionsTuple(C, string name)
// shrinkOne!args[1..$] = non-covariant others
template shrinkOne(/+ alias target, rest... +/ args...)
{
import std.meta : AliasSeq;
alias target = args[0 .. 1]; // prevent property functions from being evaluated
alias rest = args[1 .. $];

Expand Down Expand Up @@ -3809,6 +3819,7 @@ unittest

unittest // Issue 15920
{
import std.meta : AliasSeq;
class A
{
void f(){}
Expand Down Expand Up @@ -3960,13 +3971,14 @@ unittest

private template maxAlignment(U...) if (isTypeTuple!U)
{
import std.meta : staticMap;
static if (U.length == 0)
static assert(0);
else static if (U.length == 1)
enum maxAlignment = U[0].alignof;
else
{
import std.algorithm : max;
import std.algorithm.comparison : max;
enum maxAlignment = max(staticMap!(.maxAlignment, U));
}
}
Expand Down Expand Up @@ -4687,6 +4699,7 @@ unittest
*/
template IntegralTypeOf(T)
{
import std.meta : staticIndexOf;
static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT))
alias X = IntegralTypeOf!AT;
else
Expand Down Expand Up @@ -4721,6 +4734,7 @@ unittest
*/
template FloatingPointTypeOf(T)
{
import std.meta : staticIndexOf;
static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT))
alias X = FloatingPointTypeOf!AT;
else
Expand Down Expand Up @@ -4784,6 +4798,7 @@ unittest
*/
template UnsignedTypeOf(T)
{
import std.meta : staticIndexOf;
static if (is(IntegralTypeOf!T X) &&
staticIndexOf!(Unqual!X, UnsignedIntTypeList) >= 0)
alias UnsignedTypeOf = X;
Expand All @@ -4795,6 +4810,7 @@ template UnsignedTypeOf(T)
*/
template SignedTypeOf(T)
{
import std.meta : staticIndexOf;
static if (is(IntegralTypeOf!T X) &&
staticIndexOf!(Unqual!X, SignedIntTypeList) >= 0)
alias SignedTypeOf = X;
Expand All @@ -4808,6 +4824,7 @@ template SignedTypeOf(T)
*/
template CharTypeOf(T)
{
import std.meta : staticIndexOf;
static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT))
alias X = CharTypeOf!AT;
else
Expand Down Expand Up @@ -6609,7 +6626,7 @@ unittest
unittest
{
// Test for bug 5718
import std.demangle;
import std.demangle : demangle;
int foo;
auto foo_demangled = demangle(mangledName!foo);
assert(foo_demangled[0 .. 4] == "int " && foo_demangled[$-3 .. $] == "foo",
Expand Down Expand Up @@ -6680,7 +6697,7 @@ unittest
*/
template hasUDA(alias symbol, alias attribute)
{
import std.typetuple : staticIndexOf;
import std.meta : staticIndexOf, staticMap;

static if (is(attribute == struct) || is(attribute == class))
{
Expand Down Expand Up @@ -6748,7 +6765,7 @@ unittest
*/
template getUDAs(alias symbol, alias attribute)
{
import std.typetuple : Filter;
import std.meta : Filter;

template isDesiredUDA(alias S) {
static if (__traits(compiles, is(typeof(S) == attribute)))
Expand Down Expand Up @@ -6943,7 +6960,7 @@ enum ifTestable(T, alias pred = a => a) = __traits(compiles, { if (pred(T.init))

unittest
{
import std.meta : AliasSeq;
import std.meta : AliasSeq, allSatisfy;
static assert(allSatisfy!(ifTestable, AliasSeq!(bool, int, float, double, string)));
struct BoolWrapper { bool value; }
static assert(!ifTestable!(bool, a => BoolWrapper(a)));
Expand Down