Skip to content

Commit 8020102

Browse files
authored
Merge pull request #130 from rsyncOSX/version-3.0.2
Version 3.0.2
2 parents 82a9e06 + 64a97b8 commit 8020102

13 files changed

Lines changed: 270 additions & 50 deletions

File tree

RsyncUI.xcodeproj/project.pbxproj

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@
518518
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
519519
CODE_SIGN_STYLE = Automatic;
520520
COMBINE_HIDPI_IMAGES = YES;
521-
CURRENT_PROJECT_VERSION = 195;
521+
CURRENT_PROJECT_VERSION = 196;
522522
DEAD_CODE_STRIPPING = YES;
523523
DEVELOPMENT_ASSET_PATHS = "\"RsyncUI/Preview Content\"";
524524
ENABLE_HARDENED_RUNTIME = YES;
@@ -530,7 +530,7 @@
530530
"@executable_path/../Frameworks",
531531
);
532532
MACOSX_DEPLOYMENT_TARGET = 14.0;
533-
MARKETING_VERSION = 3.0.0;
533+
MARKETING_VERSION = 3.0.2;
534534
PRODUCT_BUNDLE_IDENTIFIER = no.blogspot.RsyncUI;
535535
PRODUCT_NAME = "$(TARGET_NAME)";
536536
SWIFT_APPROACHABLE_CONCURRENCY = YES;
@@ -551,7 +551,7 @@
551551
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
552552
CODE_SIGN_STYLE = Automatic;
553553
COMBINE_HIDPI_IMAGES = YES;
554-
CURRENT_PROJECT_VERSION = 195;
554+
CURRENT_PROJECT_VERSION = 196;
555555
DEAD_CODE_STRIPPING = YES;
556556
DEVELOPMENT_ASSET_PATHS = "\"RsyncUI/Preview Content\"";
557557
ENABLE_HARDENED_RUNTIME = YES;
@@ -563,7 +563,7 @@
563563
"@executable_path/../Frameworks",
564564
);
565565
MACOSX_DEPLOYMENT_TARGET = 14.0;
566-
MARKETING_VERSION = 3.0.0;
566+
MARKETING_VERSION = 3.0.2;
567567
PRODUCT_BUNDLE_IDENTIFIER = no.blogspot.RsyncUI;
568568
PRODUCT_NAME = "$(TARGET_NAME)";
569569
SWIFT_APPROACHABLE_CONCURRENCY = YES;
@@ -633,7 +633,7 @@
633633
CODE_SIGN_IDENTITY = "-";
634634
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
635635
CODE_SIGN_STYLE = Automatic;
636-
CURRENT_PROJECT_VERSION = 195;
636+
CURRENT_PROJECT_VERSION = 196;
637637
ENABLE_HARDENED_RUNTIME = YES;
638638
GCC_C_LANGUAGE_STANDARD = gnu17;
639639
GENERATE_INFOPLIST_FILE = YES;
@@ -647,7 +647,7 @@
647647
);
648648
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
649649
MACOSX_DEPLOYMENT_TARGET = 14.0;
650-
MARKETING_VERSION = 3.0.0;
650+
MARKETING_VERSION = 3.0.2;
651651
PRODUCT_BUNDLE_IDENTIFIER = no.blogspot.RsyncUI.WidgetEstimate;
652652
PRODUCT_NAME = "$(TARGET_NAME)";
653653
SKIP_INSTALL = YES;
@@ -667,7 +667,7 @@
667667
CODE_SIGN_IDENTITY = "-";
668668
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
669669
CODE_SIGN_STYLE = Automatic;
670-
CURRENT_PROJECT_VERSION = 195;
670+
CURRENT_PROJECT_VERSION = 196;
671671
ENABLE_HARDENED_RUNTIME = YES;
672672
GCC_C_LANGUAGE_STANDARD = gnu17;
673673
GENERATE_INFOPLIST_FILE = YES;
@@ -681,7 +681,7 @@
681681
);
682682
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
683683
MACOSX_DEPLOYMENT_TARGET = 14.0;
684-
MARKETING_VERSION = 3.0.0;
684+
MARKETING_VERSION = 3.0.2;
685685
PRODUCT_BUNDLE_IDENTIFIER = no.blogspot.RsyncUI.WidgetEstimate;
686686
PRODUCT_NAME = "$(TARGET_NAME)";
687687
SKIP_INSTALL = YES;

RsyncUI/Model/Execution/EstimateExecute/Estimate.swift

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,7 @@ final class Estimate {
4848
let arguments = ArgumentsSynchronize(config: config).argumentsSynchronize(dryRun: true, forDisplay: false)
4949
else { return }
5050

51-
// Check if the arguments --itemize-changes and --update are included within the arguments
52-
if arguments.contains("--itemize-changes"), arguments.contains("--update") {
53-
itemizechanges = true
54-
} else {
55-
itemizechanges = false
56-
}
51+
itemizechanges = arguments.contains("--itemize-changes")
5752
// Used to display details of configuration in estimation
5853
localprogressdetails?.configurationtobestimated = config.id
5954

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//
2+
// ItemizedOutput.swift
3+
// RsyncUI
4+
//
5+
6+
import Foundation
7+
import SwiftUI
8+
9+
enum ItemizedChangeKind: String {
10+
case added = "Added"
11+
case updated = "Updated"
12+
case deleted = "Deleted"
13+
case metadata = "Metadata"
14+
case other = "Other"
15+
16+
var systemImage: String {
17+
switch self {
18+
case .added: "plus.circle.fill"
19+
case .updated: "arrow.triangle.2.circlepath.circle.fill"
20+
case .deleted: "minus.circle.fill"
21+
case .metadata: "info.circle.fill"
22+
case .other: "questionmark.circle.fill"
23+
}
24+
}
25+
26+
var color: Color {
27+
switch self {
28+
case .added: .green
29+
case .updated: .blue
30+
case .deleted: .red
31+
case .metadata: .orange
32+
case .other: .secondary
33+
}
34+
}
35+
}
36+
37+
struct ItemizedOutputRecord {
38+
let kind: ItemizedChangeKind
39+
let path: String
40+
let code: String
41+
42+
init?(_ record: String) {
43+
let trimmed = record.trimmingCharacters(in: .whitespacesAndNewlines)
44+
guard trimmed.isEmpty == false else { return nil }
45+
46+
if trimmed.hasPrefix("*deleting") {
47+
kind = .deleted
48+
path = trimmed.replacingOccurrences(of: "*deleting", with: "")
49+
.trimmingCharacters(in: .whitespaces)
50+
code = "*deleting"
51+
return
52+
}
53+
54+
let prefixLength = Self.prefixLength(in: trimmed)
55+
guard let prefixLength else { return nil }
56+
57+
code = String(trimmed.prefix(prefixLength))
58+
path = String(trimmed.dropFirst(prefixLength))
59+
.trimmingCharacters(in: .whitespaces)
60+
guard path.isEmpty == false else { return nil }
61+
62+
let attributes = code.dropFirst(2)
63+
kind = if attributes.isEmpty == false, attributes.allSatisfy({ $0 == "+" }) {
64+
.added
65+
} else if code.first == "." {
66+
.metadata
67+
} else if code.first == ">" || code.first == "<" || code.first == "c" {
68+
.updated
69+
} else {
70+
.other
71+
}
72+
}
73+
74+
private static func prefixLength(in record: String) -> Int? {
75+
let characters = Array(record)
76+
if characters.count >= 13, characters[12] == " " {
77+
return 12
78+
}
79+
if characters.count >= 12, characters[11] == " " {
80+
return 11
81+
}
82+
if characters.count >= 10, characters[9] == " " {
83+
return 9
84+
}
85+
return nil
86+
}
87+
}
88+
89+
struct ItemizedOutputRow: View {
90+
let record: String
91+
92+
var body: some View {
93+
if let parsed = ItemizedOutputRecord(record) {
94+
HStack(spacing: 10) {
95+
Label(parsed.kind.rawValue, systemImage: parsed.kind.systemImage)
96+
.foregroundStyle(parsed.kind.color)
97+
.font(.caption.weight(.semibold))
98+
.frame(width: 92, alignment: .leading)
99+
100+
Text(parsed.path)
101+
.font(.caption.monospaced())
102+
.lineLimit(1)
103+
.textSelection(.enabled)
104+
105+
Spacer()
106+
107+
Text(parsed.code)
108+
.font(.caption2.monospaced())
109+
.foregroundStyle(.secondary)
110+
}
111+
.accessibilityElement(children: .combine)
112+
} else {
113+
Text(record)
114+
.font(.caption.monospaced())
115+
.textSelection(.enabled)
116+
}
117+
}
118+
}

RsyncUI/Model/ParametersRsync/ArgumentsSynchronize.swift

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ final class ArgumentsSynchronize {
1919
verify: false,
2020
dryrun: dryRun,
2121
keepDelete: keepdelete)
22-
return rsyncparameterssynchronize.computedArguments
22+
return RuntimeRsyncArguments.addingItemizedChanges(
23+
to: rsyncparameterssynchronize.computedArguments,
24+
forDisplay: forDisplay
25+
)
2326
} catch {
2427
return nil
2528
}
@@ -38,7 +41,10 @@ final class ArgumentsSynchronize {
3841
try rsyncparameterssynchronize.argumentsForSynchronize(forDisplay: forDisplay,
3942
verify: false,
4043
dryrun: dryRun)
41-
return rsyncparameterssynchronize.computedArguments
44+
return RuntimeRsyncArguments.addingItemizedChanges(
45+
to: rsyncparameterssynchronize.computedArguments,
46+
forDisplay: forDisplay
47+
)
4248
} catch {
4349
return nil
4450
}
@@ -47,7 +53,10 @@ final class ArgumentsSynchronize {
4753
try rsyncparameterssynchronize.argumentsForSynchronizeSnapshot(forDisplay: forDisplay,
4854
verify: false,
4955
dryrun: dryRun)
50-
return rsyncparameterssynchronize.computedArguments
56+
return RuntimeRsyncArguments.addingItemizedChanges(
57+
to: rsyncparameterssynchronize.computedArguments,
58+
forDisplay: forDisplay
59+
)
5160
} catch {
5261
return nil
5362
}
@@ -56,7 +65,10 @@ final class ArgumentsSynchronize {
5665
try rsyncparameterssynchronize.argumentsForSynchronizeRemote(forDisplay: forDisplay,
5766
verify: false,
5867
dryrun: dryRun)
59-
return rsyncparameterssynchronize.computedArguments
68+
return RuntimeRsyncArguments.addingItemizedChanges(
69+
to: rsyncparameterssynchronize.computedArguments,
70+
forDisplay: forDisplay
71+
)
6072
} catch {
6173
return nil
6274
}

RsyncUI/Model/ParametersRsync/ArgumentsVerify.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ final class ArgumentsVerify {
2121
try rsyncparameterssynchronize.argumentsForSynchronize(forDisplay: forDisplay,
2222
verify: true,
2323
dryrun: true)
24-
return rsyncparameterssynchronize.computedArguments
24+
return RuntimeRsyncArguments.addingItemizedChanges(
25+
to: rsyncparameterssynchronize.computedArguments,
26+
forDisplay: forDisplay
27+
)
2528
} catch {
2629
return nil
2730
}
@@ -30,7 +33,10 @@ final class ArgumentsVerify {
3033
try rsyncparameterssynchronize.argumentsForSynchronizeSnapshot(forDisplay: forDisplay,
3134
verify: true,
3235
dryrun: true)
33-
return rsyncparameterssynchronize.computedArguments
36+
return RuntimeRsyncArguments.addingItemizedChanges(
37+
to: rsyncparameterssynchronize.computedArguments,
38+
forDisplay: forDisplay
39+
)
3440
} catch {
3541
return nil
3642
}
@@ -39,7 +45,10 @@ final class ArgumentsVerify {
3945
try rsyncparameterssynchronize.argumentsForSynchronizeRemote(forDisplay: forDisplay,
4046
verify: true,
4147
dryrun: true)
42-
return rsyncparameterssynchronize.computedArguments
48+
return RuntimeRsyncArguments.addingItemizedChanges(
49+
to: rsyncparameterssynchronize.computedArguments,
50+
forDisplay: forDisplay
51+
)
4352
} catch {
4453
return nil
4554
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// RuntimeRsyncArguments.swift
3+
// RsyncUI
4+
//
5+
6+
enum RuntimeRsyncArguments {
7+
static func addingItemizedChanges(
8+
to arguments: [String],
9+
forDisplay: Bool
10+
) -> [String] {
11+
guard arguments.contains("--itemize-changes") == false else {
12+
return arguments
13+
}
14+
15+
let meaningfulIndices = arguments.indices.filter { arguments[$0] != " " }
16+
guard meaningfulIndices.count >= 2 else {
17+
return arguments
18+
}
19+
20+
let sourceIndex = meaningfulIndices[meaningfulIndices.count - 2]
21+
var updatedArguments = arguments
22+
updatedArguments.insert("--itemize-changes", at: sourceIndex)
23+
if forDisplay {
24+
updatedArguments.insert(" ", at: sourceIndex + 1)
25+
}
26+
return updatedArguments
27+
}
28+
}

RsyncUI/Views/Detailsview/OneTaskDetailsView.swift

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,7 @@ struct OneTaskDetailsView: View {
8787
useFileHandler: false
8888
)
8989

90-
// Check if the arguments --itemize-changes and --update are included within the arguments
91-
if arguments.contains("--itemize-changes"), arguments.contains("--update") {
92-
itemizechanges = true
93-
} else {
94-
itemizechanges = false
95-
}
90+
itemizechanges = arguments.contains("--itemize-changes")
9691

9792
do {
9893
try process.executeProcess()

RsyncUI/Views/InspectorViews/VerifyTask/VerifyTaskTabView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ struct VerifyTaskTabView: View {
137137
guard let streamingHandlers else { return }
138138
guard let arguments else { return }
139139

140-
itemizechanges = arguments.contains("--itemize-changes") && arguments.contains("--update")
140+
itemizechanges = arguments.contains("--itemize-changes")
141141

142142
let process = RsyncProcessStreaming.RsyncProcess(
143143
arguments: arguments,

RsyncUI/Views/OutputViews/DetailsView.swift

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
// Created by Thomas Evensen on 07/06/2024.
66
//
77

8-
import RsyncAnalyse
98
import SwiftUI
109

1110
struct DetailsView: View {
@@ -61,11 +60,7 @@ struct DetailsView: View {
6160
if itemizechanges {
6261
Table(records) {
6362
TableColumn("Output from rsync (\(records.count) rows)") { data in
64-
if SharedReference.shared.rsyncversion3 {
65-
RsyncOutputRowView(record: data.record)
66-
} else {
67-
OpenRsyncOutputRowView(record: data.record)
68-
}
63+
ItemizedOutputRow(record: data.record)
6964
}
7065
}
7166
} else {

RsyncUI/Views/OutputViews/OutputRsyncView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ struct OutputRsyncView: View {
1313
var body: some View {
1414
Table(output) {
1515
TableColumn("Output from rsync" + ": \(output.count) rows") { data in
16-
Text(data.record)
16+
ItemizedOutputRow(record: data.record)
1717
}
1818
}
1919
.padding()

0 commit comments

Comments
 (0)