///|
pub fn ensure_dir_recursive(path : String) -> Unit raise @fs.IOError {
  if @fs.path_exists(path) {
    if is_dir_safe(path) {
      return ()
    }
    @fs.remove_file(path)
  }
  let parent = @path.Path::dirname(@path.Path(path)).to_string()
  if parent.length() > 0 && parent != path {
    ensure_dir_recursive(parent)
  }
  if !@fs.path_exists(path) {
    @fs.create_dir(path)
  }
}

///|
pub fn remove_dir_recursive(path : String) -> Unit raise @fs.IOError {
  if !@fs.path_exists(path) {
    return ()
  }
  if is_dir_safe(path) {
    let entries = @fs.read_dir(path) catch {
      _ => []
    }
    for name in entries {
      let child = join_path(path, name)
      if is_dir_safe(child) {
        remove_dir_recursive(child)
      } else {
        @fs.remove_file(child)
      }
    }
    @fs.remove_dir(path)
  } else {
    @fs.remove_file(path)
  }
}

///|
fn is_dir_safe(path : String) -> Bool {
  try @fs.is_dir(path) catch {
    _ => false
  } noraise {
    v => v
  }
}

///|
pub fn join_path(lhs : String, rhs : String) -> String {
  @path.Path::join(@path.Path(lhs), @path.Path(rhs)).to_string()
}