haxe-files

A haxelib for consistent cross-platform filesystem operations and proper Windows-/Unix-style path handling. Supports wildcard file searches (globbing).


Keywords
cpp, cross, cs, file, files, filesystem, hashlink, hl, java, javascript, js, lua, neko, nodejs, phantomjs, php, python, vegardit, haxe, haxelib
License
Apache-2.0
Install
haxelib install haxe-files 1.2.0

Documentation

haxe-files - cross-platform filesystem operations

Build Status Release License Contributor Covenant

  1. What is it?
  2. The Path class
  3. The File class
  4. The Dir class
  5. The GlobPatterns class
  6. File watching
  7. Installation
  8. Using the latest code
  9. License

What is it?

A haxelib for consistent cross-platform filesystem operations and proper Windows-/Unix-style path handling.

All classes are located in the package hx.files or below.,

The library has been extensively unit tested (over 500 individual test cases) on the targets C++, C#, Eval, HashLink, Java, JavaScript (Node.js and PhantomJS), Lua, Neko, PHP 7 and Python 3.

Requires Haxe 3.4 or higher.

Note:

Haxe compatiblity

haxe-strings Haxe
1.0.0 to 1.2.1 3.4.2 or higher
2.0.0 or higher 4.0.5 or higher

The Path class

Instances of the hx.files.Path class represent the path to a file or directory on the local file system. It can be seen as an improved and extended version of the built-in haxe.io.Path class. In contrast to functions provided by haxe.io.Path, there are no situations where the result of a function is unspecified.

There exist two implementations:

  • haxe.io.Path.WindowsPath for Windows path, i.e. backslash is used as directory separator and paths starting with a drive letter or a UNC path are considered absolute
  • haxe.io.Path.UnixPath for Unix/Linux style paths, i.e. slash is used as directory separator and paths starting with a slash are considered absolute.
package com.example;

import hx.files.*;

class MyClass {

   static function main() {
      var p = Path.of("./mydir/myfile.txt");   // constructing a path compatible with the local operating/file-system
      var p = Path.unix("/mydir/myfile.txt");     // constructing a Unix-style path
      var p = Path.win("C:\\mydir\\myfile.txt"); // constructing a Windows-style path

      p.filename;      // returns "myfile.txt"
      p.filenameExt;   // returns "txt"
      p.filenameStem;  // returns "myfile"
      p.isAbsolute;    // returns true
      p.exists();      // returns true or false depending on physical existance of the path
      p.isFile();      // returns true if exits and points to a file
      p.isDirectory(); // returns true if exits and points to a directory
      p.parent;        // returns Path object pointing to "C:\\mydir"
      p.root;          // returns Path object pointing to "C:\\"

      // path joining
      var p = Path.win("C:\\mydir");
      p.join("project1\src");  // returns Path object pointing to "C:\\mydir\\project1\\src"

      // getting absolute path
      var p = Path.of("mydir");
      p.getAbsolutePath();   // returns the absolute path as string

      // normalizing a path
      var p = Path.unix("aaa/bbb/ccc/../../ddd");
      p.normalize();   // returns Path object pointing to "aaa/ddd"

      // ellipszing
      Path.unix("/home/user/foo/bar").ellipsize(15);           // returns "/home/.../bar"
      Path.win("C:\\Users\\Default\\Desktop\\").ellipsize(15); // returns "C:\...\Desktop"
   }
}

The File class

Instances of the hx.files.File class represent regular files on the local file system.

package com.example;

import hx.files.*;

class MyClass {

   static function main() {

      var f = Path.of("mydir/myfile.txt").toFile(); // converting a Path instance to a File instance
      var f = File.of("mydir/myfile.txt");          // creating a File instance from a String path

      f.touch();                // create an empty file or update the modification timestamp
      f.writeString("Hello ");  // sets the file's content
      f.appendString("world!");

      f.size(); // returns the file size

      f.copyTo("mydir/myfile2.txt");               // throws an exception if myfile2.txt exists already
      f.copyTo("mydir/myfile2.txt", [OVERWRITE]);  // replaces myfile2.txt if it exists already

      f.delete();  // deletes the file

      var f2 = f.moveTo("otherdir/MY_FILE.txt");
      f.exists();  // returns false
      f2.exists(); // returns true
   }
}

The Dir class

Instances of the hx.files.Dir class represent directories on the local file system.

package com.example;

import hx.files.*;

class MyClass {

   static function main() {

      var d = Path.of("myproject").toDir(); // converting a Path instance to a Dir instance
      var d = Dir.of("myproject");          // creating a Dir instance from a String path

      p.setCWD();    // changes the current working directory
      d.listDirs();  // returns an array of Dir instances for contained directories (non-recursive)
      d.listFiles(); // returns an array of File instances for contained files (non-recursive)

      d.findFiles("src/**/*.hx");          // returns an array with all Haxe files in the src dir
      d.findFiles("assets/**/*.{js,css}"); // returns an array with all JS/CSS files in the assets dir

      // recursively visit all contained files and directories
      d.walk(
         function(file) {
            trace(file);
         },
         function(dir) {
            trace(dir);
         }
      );

      d.copyTo("myproject2");                     // recursively copy the directory
      d.copyTo("myproject2", [OVERWRITE]);        // delete myproject2 and recursively copy the directory
      d.copyTo("myproject2", [MERGE]);            // merge the files and folders into myproject2 but skip conflicting files
      d.copyTo("myproject2", [MERGE, OVERWRITE]); // merge the files and folders into myproject2 and replace conflicting files

      d.delete(true);  // recursively delete the directory
   }
}

The GlobPatterns class

The hx.files.GlobPatterns class contains static methods to convert glob patterns into regular expressions.

package com.example;

import hx.files.*;

class MyClass {

   static function main() {
      GlobPatterns.toRegEx("*.txt");       // returns == "^[^\\\\^\\/]*\\.txt$"
      GlobPatterns.toRegEx("*file*");      // returns "^[^\\\\^\\/]*file[^\\\\^\\/]*$"
      GlobPatterns.toRegEx("file?.txt");   // returns "^file[^\\\\^\\/]\\.txt$"
      GlobPatterns.toRegEx("file[A-Z]");   // returns "^file[A-Z]$"
      GlobPatterns.toRegEx("file[!A-Z]");  // returns "^file[^A-Z]$"

      GlobPatterns.toEreg("src/**/*.hx").match("src/haxe/strings/Char.hx");            // returns true
      GlobPatterns.toEreg("assets/**/*.{js,css}").match("assets/theme/dark/dark.css"); // returns true
      GlobPatterns.toEreg("SystemOut[0-9].log").match("SystemOut1.log");               // returns true
      GlobPatterns.toEreg("SystemOut[!0-9].log").match("SystemOut1.log");              // returns false
   }
}

File watching

Implementations of the hx.files.watcher.FileWatcher interface allow you to monitor the file system for create/delete/change events.

The hx.files.watcher.PollingFileWatcher class scans the file system in intervals to recursively determine file changes. This is a rather inefficient way but works cross-target.

With Java7 or higher the hx.files.watcher.JavaFileWatcher based on WatcherService is available. It is more efficient but has some limitations as documented in the source code.

Other target-specific implementations can be provided in the future:

package com.example;

import hx.concurrent.executor.Executor;
import hx.files.*;
import hx.files.watcher.*;

class MyClass {

   static function main() {

      var ex = Executor.create(); // executor is used to schedule scanning tasks and
      var fw = new PollingFileWatcher(ex, 100 /*polling interval in MS*/);

      // register an event listener
      fw.subscribe(function (event) {
         switch(event) {
            case DIR_CREATED(dir):       trace('Dir created: $dir');
            case DIR_DELETED(dir):       trace('Dir deleted: $dir');
            case DIR_MODIFIED(dir, _):   trace('Dir modified: $dir');
            case FILE_CREATED(file):     trace('File created: $file');
            case FILE_DELETED(file):     trace('File deleted: $file');
            case FILE_MODIFIED(file, _): trace('File modified: $file');
         }
      });

      fw.watch("myconfig.cfg"); // watch a file
      fw.watch("assets/foo");   // recursively watch a directory

      // do some file modifications...

      // cleanup
      fw.stop();
      ex.stop();
   }
}

Installation

  1. install the library via haxelib using the command:

    haxelib install haxe-files
    
  2. use in your Haxe project

    • for OpenFL/Lime projects add <haxelib name="haxe-files" /> to your project.xml
    • for free-style projects add -lib haxe-files to your *.hxml file or as command line option when running the Haxe compiler

Using the latest code

Using haxelib git

haxelib git haxe-files https://github.com/vegardit/haxe-files master D:\haxe-projects\haxe-files

Using Git

  1. check-out the master branch

    git clone https://github.com/vegardit/haxe-files --branch master --single-branch D:\haxe-projects\haxe-files
    
  2. register the development release with haxe

    haxelib dev haxe-doctest D:\haxe-projects\haxe-files
    

Using Subversion

  1. check-out the trunk

    svn checkout https://github.com/vegardit/haxe-files/trunk D:\haxe-projects\haxe-files
    
  2. register the development release with haxe

    haxelib dev haxe-files D:\haxe-projects\haxe-files
    

License

All files are released under the Apache License 2.0.

Individual files contain the following tag instead of the full license text:

SPDX-License-Identifier: Apache-2.0

This enables machine processing of license information based on the SPDX License Identifiers that are available here: https://spdx.org/licenses/.