vibetunnel/ios/VibeTunnel/Models/FileEntry.swift
2025-06-23 14:58:11 +02:00

158 lines
5.1 KiB
Swift

import Foundation
/// Git status for a file
enum GitFileStatus: String, Codable {
case modified
case added
case deleted
case untracked
case unchanged
}
/// Represents a file or directory entry in the file system.
///
/// FileEntry contains metadata about a file or directory, including
/// its name, path, size, permissions, and modification time.
/// This model is typically used for file browser functionality.
struct FileEntry: Codable, Identifiable {
let name: String
let path: String
let isDir: Bool
let size: Int64
let mode: String
let modTime: Date
let isGitTracked: Bool?
let gitStatus: GitFileStatus?
var id: String { path }
/// Creates a new FileEntry with the given parameters.
///
/// - Parameters:
/// - name: The file name
/// - path: The full path to the file
/// - isDir: Whether this entry represents a directory
/// - size: The file size in bytes
/// - mode: The file permissions mode string
/// - modTime: The modification time
/// - isGitTracked: Whether the file is in a git repository
/// - gitStatus: The git status of the file
init(
name: String,
path: String,
isDir: Bool,
size: Int64,
mode: String,
modTime: Date,
isGitTracked: Bool? = nil,
gitStatus: GitFileStatus? = nil
) {
self.name = name
self.path = path
self.isDir = isDir
self.size = size
self.mode = mode
self.modTime = modTime
self.isGitTracked = isGitTracked
self.gitStatus = gitStatus
}
enum CodingKeys: String, CodingKey {
case name
case path
case isDir = "is_dir"
case size
case mode
case modTime = "mod_time"
case isGitTracked = "isGitTracked"
case gitStatus = "gitStatus"
}
/// Creates a FileEntry from a decoder.
///
/// - Parameter decoder: The decoder to read data from.
///
/// This custom initializer handles the special parsing of the modification
/// time from ISO8601 format, supporting both fractional and non-fractional seconds.
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
path = try container.decode(String.self, forKey: .path)
isDir = try container.decode(Bool.self, forKey: .isDir)
size = try container.decode(Int64.self, forKey: .size)
mode = try container.decode(String.self, forKey: .mode)
isGitTracked = try container.decodeIfPresent(Bool.self, forKey: .isGitTracked)
gitStatus = try container.decodeIfPresent(GitFileStatus.self, forKey: .gitStatus)
// Decode mod_time string as Date
let modTimeString = try container.decode(String.self, forKey: .modTime)
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
if let date = formatter.date(from: modTimeString) {
modTime = date
} else {
// Fallback without fractional seconds
formatter.formatOptions = [.withInternetDateTime]
if let date = formatter.date(from: modTimeString) {
modTime = date
} else {
throw DecodingError.dataCorruptedError(
forKey: .modTime,
in: container,
debugDescription: "Invalid date format"
)
}
}
}
/// Returns a human-readable file size string.
///
/// Uses binary units (KiB, MiB, GiB) for formatting.
/// Example: "1.5 MiB" for a file of 1,572,864 bytes.
var formattedSize: String {
let formatter = ByteCountFormatter()
formatter.countStyle = .binary
return formatter.string(fromByteCount: size)
}
/// Returns a relative date string for the modification time.
///
/// Formats the modification time relative to the current date.
/// Examples: "2 hours ago", "yesterday", "3 days ago".
var formattedDate: String {
let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .abbreviated
return formatter.localizedString(for: modTime, relativeTo: Date())
}
}
/// Git status information for a directory
struct GitStatus: Codable {
let isGitRepo: Bool
let branch: String?
let modified: [String]
let added: [String]
let deleted: [String]
let untracked: [String]
}
/// Represents a directory listing with its contents.
///
/// DirectoryListing contains the absolute path of a directory
/// and an array of FileEntry objects representing its contents.
struct DirectoryListing: Codable {
/// The absolute path of the directory being listed.
let absolutePath: String
/// Array of file and subdirectory entries in this directory.
let files: [FileEntry]
/// Git status information for the directory
let gitStatus: GitStatus?
enum CodingKeys: String, CodingKey {
case absolutePath = "fullPath"
case files
case gitStatus
}
}