Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

chore: add mutagen session state conversion#117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
ethanndickson merged 3 commits intomainfromethan/convert-mutagen-session
Mar 28, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,14 +20,12 @@ struct FileSyncConfig<VPN: VPNService, FS: FileSyncDaemon>: View {
}.width(min: 200, ideal: 240)
TableColumn("Workspace", value: \.agentHost)
.width(min: 100, ideal: 120)
TableColumn("Remote Path", value: \.betaPath)
TableColumn("Remote Path") { Text($0.betaPath).help($0.betaPath) }
.width(min: 100, ideal: 120)
TableColumn("Status") { $0.status.body }
TableColumn("Status") { $0.status.column.help($0.statusAndErrors) }
.width(min: 80, ideal: 100)
TableColumn("Size") { item in
Text(item.size)
}
.width(min: 60, ideal: 80)
TableColumn("Size") { Text($0.localSize.humanSizeBytes).help($0.sizeDescription) }
.width(min: 60, ideal: 80)
}
.contextMenu(forSelectionType: FileSyncSession.ID.self, menu: { _ in },
primaryAction: { selectedSessions in
Expand Down
269 changes: 254 additions & 15 deletionsCoder-Desktop/VPNLib/FileSync/FileSyncSession.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,19 +3,126 @@ import SwiftUI
public struct FileSyncSession: Identifiable {
public let id: String
public let alphaPath: String
public let name: String

public let agentHost: String
public let betaPath: String
public let status: FileSyncStatus
public let size: String

public let localSize: FileSyncSessionEndpointSize
public let remoteSize: FileSyncSessionEndpointSize

public let errors: [FileSyncError]

init(state: Synchronization_State) {
id = state.session.identifier
name = state.session.name

// If the protocol isn't what we expect for alpha or beta, show unknown
alphaPath = if state.session.alpha.protocol == Url_Protocol.local, !state.session.alpha.path.isEmpty {
state.session.alpha.path
} else {
"Unknown"
}
agentHost = if state.session.beta.protocol == Url_Protocol.ssh, !state.session.beta.host.isEmpty {
// TOOD: We need to either:
// - make this compatible with custom suffixes
// - always strip the tld
// - always keep the tld
state.session.beta.host
} else {
"Unknown"
}
betaPath = if !state.session.beta.path.isEmpty {
state.session.beta.path
} else {
"Unknown"
}

var status: FileSyncStatus = if state.session.paused {
.paused
} else {
convertSessionStatus(status: state.status)
}
if case .error = status {} else {
if state.conflicts.count > 0 {
status = .conflicts
}
}
self.status = status

localSize = .init(
sizeBytes: state.alphaState.totalFileSize,
fileCount: state.alphaState.files,
dirCount: state.alphaState.directories,
symLinkCount: state.alphaState.symbolicLinks
)
remoteSize = .init(
sizeBytes: state.betaState.totalFileSize,
fileCount: state.betaState.files,
dirCount: state.betaState.directories,
symLinkCount: state.betaState.symbolicLinks
)

errors = accumulateErrors(from: state)
}

public var statusAndErrors: String {
var out = "\(status.type)\n\n\(status.description)"
errors.forEach { out += "\n\t\($0)" }
return out
}

public var sizeDescription: String {
var out = ""
out += "Local:\n\(localSize.description(linePrefix: " "))\n\n"
out += "Remote:\n\(remoteSize.description(linePrefix: " "))"
return out
}
}

public struct FileSyncSessionEndpointSize: Equatable {
public let sizeBytes: UInt64
public let fileCount: UInt64
public let dirCount: UInt64
public let symLinkCount: UInt64

public init(sizeBytes: UInt64, fileCount: UInt64, dirCount: UInt64, symLinkCount: UInt64) {
self.sizeBytes = sizeBytes
self.fileCount = fileCount
self.dirCount = dirCount
self.symLinkCount = symLinkCount
}

public var humanSizeBytes: String {
humanReadableBytes(sizeBytes)
}

public func description(linePrefix: String = "") -> String {
var result = ""
result += linePrefix + humanReadableBytes(sizeBytes) + "\n"
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
if let formattedFileCount = numberFormatter.string(from: NSNumber(value: fileCount)) {
result += "\(linePrefix)\(formattedFileCount) file\(fileCount == 1 ? "" : "s")\n"
}
if let formattedDirCount = numberFormatter.string(from: NSNumber(value: dirCount)) {
result += "\(linePrefix)\(formattedDirCount) director\(dirCount == 1 ? "y" : "ies")"
}
if symLinkCount > 0, let formattedSymLinkCount = numberFormatter.string(from: NSNumber(value: symLinkCount)) {
result += "\n\(linePrefix)\(formattedSymLinkCount) symlink\(symLinkCount == 1 ? "" : "s")"
}
return result
}
}

public enum FileSyncStatus {
case unknown
case error(String)
case error(FileSyncErrorStatus)
case ok
case paused
caseneedsAttention(String)
case working(String)
caseconflicts
case working(FileSyncWorkingStatus)

public var color: Color {
switch self {
Expand All@@ -27,32 +134,164 @@ public enum FileSyncStatus {
.red
case .error:
.red
case .needsAttention:
case .conflicts:
.orange
case .working:
.white
.purple
}
}

public vardescription: String {
public vartype: String {
switch self {
case .unknown:
"Unknown"
case let .error(msg):
msg
case let .error(status):
status.name
case .ok:
"Watching"
case .paused:
"Paused"
case let .needsAttention(msg):
msg
case let .working(msg):
msg
case .conflicts:
"Conflicts"
case let .working(status):
status.name
}
}

public var description: String {
switch self {
case .unknown:
"Unknown status message."
case let .error(status):
status.description
case .ok:
"The session is watching for filesystem changes."
case .paused:
"The session is paused."
case .conflicts:
"The session has conflicts that need to be resolved."
case let .working(status):
status.description
}
}

public var column: some View {
Text(type).foregroundColor(color)
}
}

public enum FileSyncWorkingStatus {
case connectingAlpha
case connectingBeta
case scanning
case reconciling
case stagingAlpha
case stagingBeta
case transitioning
case saving

var name: String {
switch self {
case .connectingAlpha:
"Connecting (alpha)"
case .connectingBeta:
"Connecting (beta)"
case .scanning:
"Scanning"
case .reconciling:
"Reconciling"
case .stagingAlpha:
"Staging (alpha)"
case .stagingBeta:
"Staging (beta)"
case .transitioning:
"Transitioning"
case .saving:
"Saving"
}
}

var description: String {
switch self {
case .connectingAlpha:
"The session is attempting to connect to the alpha endpoint."
case .connectingBeta:
"The session is attempting to connect to the beta endpoint."
case .scanning:
"The session is scanning the filesystem on each endpoint."
case .reconciling:
"The session is performing reconciliation."
case .stagingAlpha:
"The session is staging files on the alpha endpoint"
case .stagingBeta:
"The session is staging files on the beta endpoint"
case .transitioning:
"The session is performing transition operations on each endpoint."
case .saving:
"The session is recording synchronization history to disk."
}
}
}

public enum FileSyncErrorStatus {
case disconnected
case haltedOnRootEmptied
case haltedOnRootDeletion
case haltedOnRootTypeChange
case waitingForRescan

var name: String {
switch self {
case .disconnected:
"Disconnected"
case .haltedOnRootEmptied:
"Halted on root emptied"
case .haltedOnRootDeletion:
"Halted on root deletion"
case .haltedOnRootTypeChange:
"Halted on root type change"
case .waitingForRescan:
"Waiting for rescan"
}
}

var description: String {
switch self {
case .disconnected:
"The session is unpaused but not currently connected or connecting to either endpoint."
case .haltedOnRootEmptied:
"The session is halted due to the root emptying safety check."
case .haltedOnRootDeletion:
"The session is halted due to the root deletion safety check."
case .haltedOnRootTypeChange:
"The session is halted due to the root type change safety check."
case .waitingForRescan:
"The session is waiting to retry scanning after an error during the previous scan."
}
}
}

public var body: some View {
Text(description).foregroundColor(color)
public enum FileSyncEndpoint {
case local
case remote
}

public enum FileSyncProblemType {
case scan
case transition
}

public enum FileSyncError {
case generic(String)
case problem(FileSyncEndpoint, FileSyncProblemType, path: String, error: String)

var description: String {
switch self {
case let .generic(error):
error
case let .problem(endpoint, type, path, error):
"\(endpoint) \(type) error at \(path): \(error)"
}
}
}

Expand Down
59 changes: 59 additions & 0 deletionsCoder-Desktop/VPNLib/FileSync/MutagenConvert.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
// swiftlint:disable:next cyclomatic_complexity
func convertSessionStatus(status: Synchronization_Status) -> FileSyncStatus {
switch status {
case .disconnected:
.error(.disconnected)
case .haltedOnRootEmptied:
.error(.haltedOnRootEmptied)
case .haltedOnRootDeletion:
.error(.haltedOnRootDeletion)
case .haltedOnRootTypeChange:
.error(.haltedOnRootTypeChange)
case .waitingForRescan:
.error(.waitingForRescan)
case .connectingAlpha:
.working(.connectingAlpha)
case .connectingBeta:
.working(.connectingBeta)
case .scanning:
.working(.scanning)
case .reconciling:
.working(.reconciling)
case .stagingAlpha:
.working(.stagingAlpha)
case .stagingBeta:
.working(.stagingBeta)
case .transitioning:
.working(.transitioning)
case .saving:
.working(.saving)
case .watching:
.ok
case .UNRECOGNIZED:
.unknown
}
}

func accumulateErrors(from state: Synchronization_State) -> [FileSyncError] {
var errors: [FileSyncError] = []
if !state.lastError.isEmpty {
errors.append(.generic(state.lastError))
}
for problem in state.alphaState.scanProblems {
errors.append(.problem(.local, .scan, path: problem.path, error: problem.error))
}
for problem in state.alphaState.transitionProblems {
errors.append(.problem(.local, .transition, path: problem.path, error: problem.error))
}
for problem in state.betaState.scanProblems {
errors.append(.problem(.remote, .scan, path: problem.path, error: problem.error))
}
for problem in state.betaState.transitionProblems {
errors.append(.problem(.remote, .transition, path: problem.path, error: problem.error))
}
return errors
}

func humanReadableBytes(_ bytes: UInt64) -> String {
ByteCountFormatter().string(fromByteCount: Int64(bytes))
}
File renamed without changes.
Loading

[8]ページ先頭

©2009-2025 Movatter.jp