<commit_before>package thx;

using thx.Arrays;
using thx.Strings;

/**
`Path` represents a pointer to a directory or file in a filesystem.

It supports both win32 and (U)nix style of path and tries to guess
the correct type when parsing a string. If the guessing is not possible
it assumes the Nix style by default.

Path can be conveted from/to styles using `toWin32` and `toNix`.

Path are immutable objects and can combined using the `/` operator.

Ex.
```haxe
var p : Path = "/usr/local",
    dir = "haxe";
trace(p / dir); // prints "/usr/local/haxe"
```
*/
abstract Path(Array<String>) {
  inline public static var nixSeparator : String = "/";
  inline public static var win32Separator : String = "\\";

  public static function isValidNix(path : Path) : Bool
    return path.path.any(function(_) return !_.contains("/"));

  public static function isValidWin32(path : Path) : Bool
    return path.path.any(function(_) return !(~/[<>:\/\|?*"]/g).match(_));

  public static function normalizeNix(path : Path, ?replacement = "_")
    return path.map(function(_) return _.replace("/", replacement));

  public static function normalizeWin32(path : Path, ?replacement = "_")
    return path.map(function(_) return (~/[<>:"\/\|?*]/g).replace(_, replacement));

/**
Creates an instance of `Path` from a string.
*/
  @:from
  public static function fromString(s : String) : Path {
    if(s.contains(win32Separator)) {
      var re = ~/^([a-z]+[:][\\])/i;
      if(re.match(s)) {
        return create(
          re.matched(1),
          re.matchedRight().split(win32Separator),
          win32Separator);
      } else {
        return create("", s.split(win32Separator), win32Separator);
      }
    } else {
      return create(
        s.startsWith(nixSeparator) ? nixSeparator : "",
        s.split(nixSeparator),
        nixSeparator);
    }
  }

  public var path(get, never) : Array<String>;
  public var root(get, never) : String;
  public var sep(get, never) : String;

  #if java inline #end //WHY????
  public static function resolve(path : Array<String>, isAbsolute : Bool) {
    // removes .
    path = path.compact().filter(function(s) return s != ".");
    // simplify ..
    return path.reduce(function(acc : Array<String>, s : String) {
      if(s == ".." && acc.length > 0 && acc.last() != "..") {
        return acc.slice(0, acc.length-1);
      } else if(s == ".." && isAbsolute) {
        return acc;
      } else {
        return acc.concat([s]);
      }
    }, []);
  }

  inline public static function create(root : String, path : Array<String>, sep : String) : Path
    return new Path([sep, root].concat(resolve(path, root != "")));

  inline public static function raw(parts : Array<String>)
    return new Path(parts);

  inline function new(parts : Array<String>) : Void
    this = parts;

  public function asAbsolute(?root = "C:\\")
    return create(
      sep == nixSeparator ? nixSeparator : root,
      path,
      sep
    );

  inline public function asRelative()
    return create("", path, sep);

  public function normalize()
    return isWin32() ? normalizeWin32(get_self()) : normalizeNix(get_self());

  inline public function isAbsolute()
    return root != "";

  inline public function isRelative()
    return root == "";

  public function isRoot()
    return isAbsolute() && path.length == 0;

  inline public function isNix()
    return sep == nixSeparator;

  inline public function isWin32()
    return sep == win32Separator;

  public function isValid()
    return isWin32() ? isValidWin32(get_self()) : isValidNix(get_self());

  public function base(?end : String) : String {
    if(path.length == 0)
      return '';
    var name = path.last();
    if(null != end && name.endsWith(end))
      return name.substring(0, name.length - end.length);
    return name;
  }

  public function ext() : String {
    if(path.length == 0)
      return '';
    return path.last().afterLast(".");
  }

  public function dir() : String
    return up().toString();

  public function map(handler : String -> String) : Path<commit_msg>added Path.noext()<commit_after>package thx;

using thx.Arrays;
using thx.Strings;

/**
`Path` represents a pointer to a directory or file in a filesystem.

It supports both win32 and (U)nix style of path and tries to guess
the correct type when parsing a string. If the guessing is not possible
it assumes the Nix style by default.

Path can be conveted from/to styles using `toWin32` and `toNix`.

Path are immutable objects and can combined using the `/` operator.

Ex.
```haxe
var p : Path = "/usr/local",
    dir = "haxe";
trace(p / dir); // prints "/usr/local/haxe"
```
*/
abstract Path(Array<String>) {
  inline public static var nixSeparator : String = "/";
  inline public static var win32Separator : String = "\\";

  public static function isValidNix(path : Path) : Bool
    return path.path.any(function(_) return !_.contains("/"));

  public static function isValidWin32(path : Path) : Bool
    return path.path.any(function(_) return !(~/[<>:\/\|?*"]/g).match(_));

  public static function normalizeNix(path : Path, ?replacement = "_")
    return path.map(function(_) return _.replace("/", replacement));

  public static function normalizeWin32(path : Path, ?replacement = "_")
    return path.map(function(_) return (~/[<>:"\/\|?*]/g).replace(_, replacement));

/**
Creates an instance of `Path` from a string.
*/
  @:from
  public static function fromString(s : String) : Path {
    if(s.contains(win32Separator)) {
      var re = ~/^([a-z]+[:][\\])/i;
      if(re.match(s)) {
        return create(
          re.matched(1),
          re.matchedRight().split(win32Separator),
          win32Separator);
      } else {
        return create("", s.split(win32Separator), win32Separator);
      }
    } else {
      return create(
        s.startsWith(nixSeparator) ? nixSeparator : "",
        s.split(nixSeparator),
        nixSeparator);
    }
  }

  public var path(get, never) : Array<String>;
  public var root(get, never) : String;
  public var sep(get, never) : String;

  #if java inline #end //WHY????
  public static function resolve(path : Array<String>, isAbsolute : Bool) {
    // removes .
    path = path.compact().filter(function(s) return s != ".");
    // simplify ..
    return path.reduce(function(acc : Array<String>, s : String) {
      if(s == ".." && acc.length > 0 && acc.last() != "..") {
        return acc.slice(0, acc.length-1);
      } else if(s == ".." && isAbsolute) {
        return acc;
      } else {
        return acc.concat([s]);
      }
    }, []);
  }

  inline public static function create(root : String, path : Array<String>, sep : String) : Path
    return new Path([sep, root].concat(resolve(path, root != "")));

  inline public static function raw(parts : Array<String>)
    return new Path(parts);

  inline function new(parts : Array<String>) : Void
    this = parts;

  public function asAbsolute(?root = "C:\\")
    return create(
      sep == nixSeparator ? nixSeparator : root,
      path,
      sep
    );

  inline public function asRelative()
    return create("", path, sep);

  public function normalize()
    return isWin32() ? normalizeWin32(get_self()) : normalizeNix(get_self());

  inline public function isAbsolute()
    return root != "";

  inline public function isRelative()
    return root == "";

  public function isRoot()
    return isAbsolute() && path.length == 0;

  inline public function isNix()
    return sep == nixSeparator;

  inline public function isWin32()
    return sep == win32Separator;

  public function isValid()
    return isWin32() ? isValidWin32(get_self()) : isValidNix(get_self());

  public function noext() : String {
    var e = ext();
    if(e == "")
      return base();
    else
      return base('.$e');
  }

  public function base(?end : String) : String {
    if(path.length == 0)
      return '';
    var name = path.last();
    if(null != end && name.endsWith(end))
      return name.substring(0, name.length - end.length);
    return name;
  }

  public function ext() : String {
    if(path.length == 0)
      return '';
    return path.last().afterLast(".");
  }

  public function dir() : String
    return up().toString();

  public function map(handler : String -> String) : Path
{'fraction_non_alphanumeric': 0.15051944914230492, 'fraction_numerical': 0.009664170089393574, 'mean_word_length': 3.007260406582769, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3167658', 'n_tokens_mistral': 2612, 'n_tokens_neox': 2500, 'n_words': 751}