// retoor import "io" for Directory, File, Stat import "os" for Platform, Process import "scheduler" for Scheduler class PurePath { construct new(path) { if (path is PurePath) { _path = path.toString } else if (path is String) { _path = path } else { Fiber.abort("Path must be a string or Path.") } } toString { _path } parts { if (_path.isEmpty) return [] var result = [] var current = "" var i = 0 if (_path[0] == "/") { result.add("/") i = 1 } while (i < _path.count) { var c = _path[i] if (c == "/") { if (!current.isEmpty) { result.add(current) current = "" } } else { current = current + c } i = i + 1 } if (!current.isEmpty) { result.add(current) } return result } name { if (_path.isEmpty) return "" var p = parts if (p.isEmpty) return "" return p[-1] } suffix { var n = name if (n.isEmpty) return "" var dotIndex = -1 for (i in (n.count - 1)..0) { if (n[i] == ".") { dotIndex = i break } } if (dotIndex <= 0) return "" return n[dotIndex..-1] } suffixes { var n = name if (n.isEmpty) return [] var result = [] var i = n.count - 1 while (i > 0) { if (n[i] == ".") { result.insert(0, n[i..-1]) n = n[0...i] i = n.count - 1 } else { i = i - 1 } } return result } stem { var n = name if (n.isEmpty) return "" var s = suffix if (s.isEmpty) return n return n[0...(n.count - s.count)] } parent { if (_path.isEmpty) return Path.new(".") var p = parts if (p.count <= 1) { if (isAbsolute) return Path.new("/") return Path.new(".") } p = p[0...-1] return Path.new(joinParts_(p)) } parents { var result = [] var current = this while (true) { var p = current.parent if (p.toString == current.toString) break result.add(p) current = p } return result } drive { if (_path.count >= 2 && _path[1] == ":") { return _path[0..1] } return "" } root { if (_path.isEmpty) return "" if (_path[0] == "/") return "/" if (drive != "" && _path.count >= 3 && _path[2] == "/") return "/" return "" } anchor { drive + root } isAbsolute { root != "" } asPosix { var result = "" for (i in 0..._path.count) { var c = _path[i] if (c == "\\") { result = result + "/" } else { result = result + c } } return result } joinpath(other) { if (other is List) { var result = this for (p in other) { result = result.joinpath(p) } return result } var otherPath = other is PurePath ? other.toString : other.toString if (otherPath.isEmpty) return Path.new(_path) if (otherPath[0] == "/") return Path.new(otherPath) if (_path.isEmpty) return Path.new(otherPath) if (_path[-1] == "/") return Path.new(_path + otherPath) return Path.new(_path + "/" + otherPath) } /(other) { joinpath(other) } withName(newName) { if (name.isEmpty) Fiber.abort("Path has no name.") var p = parent return p / newName } withStem(newStem) { var s = suffix return withName(newStem + s) } withSuffix(newSuffix) { var s = stem if (s.isEmpty) Fiber.abort("Path has no stem.") return withName(s + newSuffix) } relativeTo(base) { var basePath = base is PurePath ? base : PurePath.new(base) var myParts = parts var baseParts = basePath.parts if (baseParts.count > myParts.count) { Fiber.abort("'%(this)' is not relative to '%(basePath)'") } for (i in 0...baseParts.count) { if (myParts[i] != baseParts[i]) { Fiber.abort("'%(this)' is not relative to '%(basePath)'") } } if (baseParts.count == myParts.count) return Path.new(".") var relParts = myParts[baseParts.count..-1] return Path.new(joinParts_(relParts)) } match(pattern) { var n = name return matchGlob_(n, pattern) } ==(other) { if (!(other is PurePath)) return false return _path == other.toString } !=(other) { !(this == other) } hashCode { _path.hashCode } joinParts_(p) { if (p.isEmpty) return "" if (p.count == 1 && p[0] == "/") return "/" var result = "" for (i in 0...p.count) { if (i > 0 && p[i] != "/") { if (result != "" && result[-1] != "/") { result = result + "/" } } result = result + p[i] } return result } matchGlob_(text, pattern) { var ti = 0 var pi = 0 var starTi = -1 var starPi = -1 while (ti < text.count) { if (pi < pattern.count && (pattern[pi] == "?" || pattern[pi] == text[ti])) { ti = ti + 1 pi = pi + 1 } else if (pi < pattern.count && pattern[pi] == "*") { starPi = pi starTi = ti pi = pi + 1 } else if (starPi >= 0) { pi = starPi + 1 starTi = starTi + 1 ti = starTi } else { return false } } while (pi < pattern.count && pattern[pi] == "*") { pi = pi + 1 } return pi == pattern.count } } class Path is PurePath { construct new(path) { super(path) } static cwd { Path.new(Process.cwd) } static home { Path.new(Platform.homePath) } exists() { var stat Fiber.new { stat = Stat.path(toString) }.try() return stat != null } isFile() { var stat Fiber.new { stat = Stat.path(toString) }.try() if (stat == null) return false return stat.isFile } isDir() { var stat Fiber.new { stat = Stat.path(toString) }.try() if (stat == null) return false return stat.isDirectory } foreign static isSymlink_(path, fiber) isSymlink() { return Scheduler.await_ { Path.isSymlink_(toString, Fiber.current) } } stat() { Stat.path(toString) } foreign static lstat_(path, fiber) lstat() { return Scheduler.await_ { Path.lstat_(toString, Fiber.current) } } readText() { File.read(toString) } readBytes() { File.read(toString) } writeText(content) { File.create(toString) {|f| f.writeBytes(content, 0) } } writeBytes(content) { File.create(toString) {|f| f.writeBytes(content, 0) } } unlink() { File.delete(toString) } rmdir() { Directory.delete(toString) } mkdir() { mkdir(false) } mkdir(parents) { if (parents) { var current = Path.new("") for (part in parts) { current = current / part if (!current.exists()) { Directory.create(current.toString) } } } else { Directory.create(toString) } } iterdir() { var entries = Directory.list(toString) var result = [] for (entry in entries) { result.add(this / entry) } return result } resolve() { var realPath = File.realPath(toString) return Path.new(realPath) } expanduser() { var s = toString if (s.isEmpty) return this if (s[0] == "~") { if (s.count == 1) { return Path.home } if (s[1] == "/") { return Path.home / s[2..-1] } } return this } foreign static touch_(path, fiber) touch() { if (!exists()) { File.create(toString) {|f| } } else { Scheduler.await_ { Path.touch_(toString, Fiber.current) } } } foreign static rename_(oldPath, newPath, fiber) rename(target) { var targetPath = target is Path ? target.toString : target.toString Scheduler.await_ { Path.rename_(toString, targetPath, Fiber.current) } return Path.new(targetPath) } replace(target) { rename(target) } foreign static chmod_(path, mode, fiber) chmod(mode) { Scheduler.await_ { Path.chmod_(toString, mode, Fiber.current) } } foreign static symlinkTo_(link, target, fiber) symlinkTo(target) { var targetPath = target is Path ? target.toString : target.toString Scheduler.await_ { Path.symlinkTo_(toString, targetPath, Fiber.current) } } foreign static hardlinkTo_(link, target, fiber) hardlinkTo(target) { var targetPath = target is Path ? target.toString : target.toString Scheduler.await_ { Path.hardlinkTo_(toString, targetPath, Fiber.current) } } foreign static readlink_(path, fiber) readlink() { return Scheduler.await_ { Path.readlink_(toString, Fiber.current) } } foreign static samefile_(path1, path2, fiber) samefile(other) { var otherPath = other is Path ? other.toString : other.toString return Scheduler.await_ { Path.samefile_(toString, otherPath, Fiber.current) } } foreign static glob_(basePath, pattern, recursive, fiber) glob(pattern) { var results = Scheduler.await_ { Path.glob_(toString, pattern, false, Fiber.current) } var paths = [] for (r in results) { paths.add(Path.new(r)) } return paths } rglob(pattern) { var results = Scheduler.await_ { Path.glob_(toString, pattern, true, Fiber.current) } var paths = [] for (r in results) { paths.add(Path.new(r)) } return paths } foreign static owner_(path, fiber) owner() { return Scheduler.await_ { Path.owner_(toString, Fiber.current) } } foreign static group_(path, fiber) group() { return Scheduler.await_ { Path.group_(toString, Fiber.current) } } walk() { walk(true) } walk(topDown) { var results = [] walkRecursive_(this, results, topDown) return results } walkRecursive_(dir, results, topDown) { var files = [] var dirs = [] for (entry in dir.iterdir()) { if (entry.isDir()) { dirs.add(entry.name) } else { files.add(entry.name) } } if (topDown) { results.add([dir, dirs, files]) for (d in dirs) { walkRecursive_(dir / d, results, topDown) } } else { for (d in dirs) { walkRecursive_(dir / d, results, topDown) } results.add([dir, dirs, files]) } } rmtree() { for (entry in walk(false)) { var root = entry[0] var files = entry[2] for (f in files) { (root / f).unlink() } root.rmdir() } } copyfile(dest) { var content = readBytes() var destPath = dest is Path ? dest : Path.new(dest) destPath.writeBytes(content) } }