/*
* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* This source file is part of the Cangjie project, licensed under Apache-2.0
* with Runtime Library Exception.
*
* See https://cangjie-lang.cn/pages/LICENSE for license information.
*/
// The Cangjie API is in Beta. For details on its capabilities and limitations, please refer to the README file.
/**
* @file
*
* This file defines FileInfo classes.
*/
package std.fs
import std.time.*
public struct FileInfo <: Equatable<FileInfo> {
var _path: Path
public init(path: Path) {
this(path, true)
}
public init(path: String) {
this(Path(path), true)
}
/**
* @throws FSException if path is invalid
*/
init(path: Path, isCheckPath: Bool) {
if (isCheckPath && !exists(path)) {
throw FSException("The input path `${path}` does not exist.")
}
_path = toAbsolutePath(path)
}
public prop name: String {
get() {
_path.fileName
}
}
public prop parentDirectory: Option<FileInfo> {
get() {
let parent = _path.parent
if (!parent.toString().isEmpty() && !isRootPath(_path.toString())) {
return FileInfo(parent)
}
return None
}
}
public prop path: Path {
get() {
_path
}
}
/**
* @throws FSException if system failed to get creation time
*/
public prop creationTime: DateTime {
get() {
let time = callNativeFunc(CJ_FS_GetCreationTime, "Failed to get creation time.")
return DateTime.ofEpoch(second: time, nanosecond: 0)
}
}
/**
* @throws FSException if system failed to get last access time
*/
public prop lastAccessTime: DateTime {
get() {
let time = callNativeFunc(CJ_FS_GetLastAccessTime, "Failed to get last access time.")
return DateTime.ofEpoch(second: time, nanosecond: 0)
}
}
/**
* @throws FSException if system failed to get last modification time
*/
public prop lastModificationTime: DateTime {
get() {
let time = callNativeFunc(CJ_FS_GetLastModificationTime, "Failed to get last modification time.")
return DateTime.ofEpoch(second: time, nanosecond: 0)
}
}
/**
* @throws FSException if memory copy failed or get directory/file size failed
*/
public prop size: Int64 {
get() {
return callNativeFunc(CJ_FS_PathSize, "Failed to get file size.")
}
}
/**
* @throws FSException if cPath is invalid or symbol not linked
*/
public func isSymbolicLink(): Bool {
return callNativeFunc(CJ_FS_IsLink)
}
/**
* @throws FSException if cPath is invalid
*/
public func isRegular(): Bool {
return callNativeFunc(CJ_FS_IsFile)
}
/**
* @throws FSException if cPath is invalid
*/
public func isDirectory(): Bool {
return callNativeFunc(CJ_FS_IsDir)
}
/**
* @throws FSException if cPath is invalid
*/
public func isReadOnly(): Bool {
return callNativeFunc(CJ_FS_IsReadOnly)
}
public func isHidden(): Bool {
let fileName = canonicalize(_path).fileName
if (fileName.isEmpty()) {
throw FSException("FileName is empty.")
}
return fileName[0] == b'.'
}
/**
* @throws FSException if cPath is invalid
*/
public func canExecute(): Bool {
// _wstat in implementation do not accept "\\\\?\\" as it's argument
return callNativeFunc(CJ_FS_CanExecute, shouldFixLongPath: false)
}
/**
* @throws FSException if cPath is invalid
*/
public func canRead(): Bool {
return callNativeFunc(CJ_FS_CanRead)
}
/**
* @throws FSException if cPath is invalid
*/
public func canWrite(): Bool {
return callNativeFunc(CJ_FS_CanWrite)
}
/**
* @throws FSException if cPath is invalid
* @throws FSException if operation not permitted on Windows
* Processes running as a privileged user may bypass this permission setting.
*/
public func setExecutable(executable: Bool): Bool {
return callNativeFunc(CJ_FS_SetExecutable, executable)
}
/**
* @throws FSException if cPath is invalid
* @throws FSException if operation not permitted on Windows
* Processes running as a privileged user may bypass this permission setting.
*/
public func setReadable(readable: Bool): Bool {
return callNativeFunc(CJ_FS_SetReadable, readable)
}
/**
* @throws FSException if cPath is invalid
* @throws FSException if operation not permitted on Windows
*/
public func setWritable(writable: Bool): Bool {
// _wstat in implementation do not accept "\\\\?\\" as it's argument
return callNativeFunc(CJ_FS_SetWritable, writable, shouldFixLongPath: false)
}
/**
* @throws FSException if cPath is invalid
*/
private func callNativeFunc(nativeFunction: CFunc<(CString) -> Int8>, shouldFixLongPath!: Bool = true): Bool {
unsafe {
let pathStr = if (shouldFixLongPath) {
fixLongPath(_path.toString())
} else {
_path.toString()
}
let cPath = LibC.mallocCString(pathStr)
var ret: Int8 = nativeFunction(cPath)
LibC.free(cPath)
if (ret < 0) {
throw FSException("Native function error return ${ret}.")
}
return ret > 0
}
}
/**
* @throws FSException if cPath is invalid
* @throws FSException if operation not permitted on Windows
*/
private func callNativeFunc(nativeFunction: CFunc<(CString, Bool) -> Int8>, flag: Bool,
shouldFixLongPath!: Bool = true): Bool {
unsafe {
let pathStr = if (shouldFixLongPath) {
fixLongPath(_path.toString())
} else {
_path.toString()
}
let cPath = LibC.mallocCString(pathStr)
let ret: Int8 = nativeFunction(cPath, flag)
LibC.free(cPath)
if (ret == -1) {
throw FSException("Native function error return ${ret}.")
} else if (ret == -4) {
throw FSException("Operation not permitted!")
} else if (ret == -2) {
throw FSException("Invalid argument!")
}
return ret > 0
}
}
/**
* @throws FSException if `nativeFunction` returns negative error code
*/
private func callNativeFunc(nativeFunction: CFunc<(CString) -> Int64>, msg: String, shouldFixLongPath!: Bool = true): Int64 {
unsafe {
let pathStr = if (shouldFixLongPath) {
fixLongPath(_path.toString())
} else {
_path.toString()
}
let cPath: CString = LibC.mallocCString(pathStr)
let ret: Int64 = nativeFunction(cPath)
LibC.free(cPath)
if (ret < 0) {
throw FSException("${msg} Native function return ${ret}.")
}
return ret
}
}
public operator func ==(other: FileInfo): Bool {
return _path == other.path
}
}