Skip to content

Commit 1fa7919

Browse files
committed
#421: Add option to skip unconstructive backups
1 parent 097281e commit 1fa7919

File tree

11 files changed

+107
-8
lines changed

11 files changed

+107
-8
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
This can be used to satisfy the `<base>` and `<game>` path placeholders
99
in cases where Ludusavi can't automatically detect the right folder.
1010
For more info, [see the custom games document](/docs/help/custom-games.md).
11+
* On the "other" screen,
12+
there is a new option to skip backups when saves are only removed but not added/updated.
13+
This can be useful because uninstalling a game may cause some of its data (but not all) to be removed,
14+
but you may not want to exclude that data from your backups yet.
1115
* CLI: `config show` command.
1216
* CLI: The `backup`, `restore`, `cloud upload`, and `cloud download` commands
1317
now support a `--gui` option for graphical dialog prompts.

lang/en-US.ftl

+1
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ show-unscanned-games = Show unscanned games
211211
override-max-threads = Override max threads
212212
synchronize-automatically = Synchronize automatically
213213
prefer-alias-display = Display alias instead of original name
214+
skip-unconstructive-backups = Skip backup when data would be removed, but not added or updated
214215
215216
explanation-for-exclude-store-screenshots =
216217
In backups, exclude store-specific screenshots

src/cli.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ pub fn run(sub: Subcommand, no_manifest_update: bool, try_manifest_update: bool)
248248
&config.redirects,
249249
config.restore.reverse_redirects,
250250
&config.restore.toggled_paths,
251+
config.backup.only_constructive,
251252
);
252253

253254
if filter.excludes(games_specified, previous.is_some(), &game.cloud) {
@@ -269,6 +270,7 @@ pub fn run(sub: Subcommand, no_manifest_update: bool, try_manifest_update: bool)
269270
&config.redirects,
270271
config.restore.reverse_redirects,
271272
&steam_shortcuts,
273+
config.backup.only_constructive,
272274
);
273275
let ignored = !&config.is_game_enabled_for_backup(name) && !games_specified;
274276
let decision = if ignored {
@@ -292,9 +294,13 @@ pub fn run(sub: Subcommand, no_manifest_update: bool, try_manifest_update: bool)
292294
.set_level(&backup_format.zip.compression, level);
293295
}
294296

295-
layout
296-
.game_layout(name)
297-
.back_up(&scan_info, &chrono::Utc::now(), &backup_format, retention)
297+
layout.game_layout(name).back_up(
298+
&scan_info,
299+
&chrono::Utc::now(),
300+
&backup_format,
301+
retention,
302+
config.backup.only_constructive,
303+
)
298304
};
299305
log::trace!("step {i} completed");
300306
if !scan_info.can_report_game() {

src/gui/app.rs

+9
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ impl App {
487487
&config.redirects,
488488
config.restore.reverse_redirects,
489489
&config.restore.toggled_paths,
490+
config.backup.only_constructive,
490491
);
491492

492493
if filter.excludes(games_specified, previous.is_some(), &game.cloud) {
@@ -508,6 +509,7 @@ impl App {
508509
&config.redirects,
509510
config.restore.reverse_redirects,
510511
&steam_shortcuts,
512+
config.backup.only_constructive,
511513
);
512514
if !config.is_game_enabled_for_backup(&key) && !single {
513515
return (Some(scan_info), None);
@@ -519,6 +521,7 @@ impl App {
519521
&chrono::Utc::now(),
520522
&config.backup.format,
521523
retention,
524+
config.backup.only_constructive,
522525
)
523526
} else {
524527
None
@@ -1880,6 +1883,12 @@ impl App {
18801883
.custom_games
18811884
.sort_by(|x, y| x.name.current().cmp(&y.name.current()));
18821885
}
1886+
config::Event::OnlyConstructiveBackups(value) => {
1887+
self.config.backup.only_constructive = value;
1888+
for entry in &mut self.backup_screen.log.entries {
1889+
entry.scan_info.only_constructive_backups = value;
1890+
}
1891+
}
18831892
}
18841893

18851894
self.save_config();

src/gui/screen.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,12 @@ pub fn other<'a>(
577577
_ => None,
578578
},
579579
),
580-
),
580+
)
581+
.push(Row::new().spacing(5).align_y(Alignment::Center).push(checkbox(
582+
TRANSLATOR.skip_unconstructive_backups(),
583+
config.backup.only_constructive,
584+
Message::config(config::Event::OnlyConstructiveBackups),
585+
))),
581586
)
582587
.class(style::Container::GameListEntry),
583588
),

src/lang.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1277,6 +1277,10 @@ impl Translator {
12771277
translate("prefer-alias-display")
12781278
}
12791279

1280+
pub fn skip_unconstructive_backups(&self) -> String {
1281+
translate("skip-unconstructive-backups")
1282+
}
1283+
12801284
pub fn total_games(&self) -> String {
12811285
translate("total-games")
12821286
}

src/resource/config.rs

+9
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ pub enum Event {
9797
CloudRemoteId(String),
9898
CloudPath(String),
9999
SortCustomGames,
100+
OnlyConstructiveBackups(bool),
100101
}
101102

102103
/// Settings for `config.yaml`
@@ -1108,6 +1109,8 @@ pub struct BackupConfig {
11081109
pub sort: Sort,
11091110
pub retention: Retention,
11101111
pub format: BackupFormats,
1112+
/// Don't create a new backup if there are only removed saves and no new/edited ones.
1113+
pub only_constructive: bool,
11111114
}
11121115

11131116
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
@@ -1320,6 +1323,7 @@ impl Default for BackupConfig {
13201323
sort: Default::default(),
13211324
retention: Retention::default(),
13221325
format: Default::default(),
1326+
only_constructive: Default::default(),
13231327
}
13241328
}
13251329
}
@@ -2069,6 +2073,7 @@ mod tests {
20692073
sort: Default::default(),
20702074
retention: Retention::default(),
20712075
format: Default::default(),
2076+
only_constructive: false,
20722077
},
20732078
restore: RestoreConfig {
20742079
path: StrictPath::relative(s("~/restore"), Some(StrictPath::cwd().render())),
@@ -2120,6 +2125,7 @@ mod tests {
21202125
- Backup Game 2
21212126
filter:
21222127
excludeStoreScreenshots: true
2128+
onlyConstructive: true
21232129
restore:
21242130
path: ~/restore
21252131
ignoredGames:
@@ -2194,6 +2200,7 @@ mod tests {
21942200
sort: Default::default(),
21952201
retention: Retention::default(),
21962202
format: Default::default(),
2203+
only_constructive: true,
21972204
},
21982205
restore: RestoreConfig {
21992206
path: StrictPath::relative(s("~/restore"), Some(StrictPath::cwd().render())),
@@ -2316,6 +2323,7 @@ backup:
23162323
level: 6
23172324
zstd:
23182325
level: 10
2326+
onlyConstructive: false
23192327
restore:
23202328
path: ~/restore
23212329
ignoredGames:
@@ -2402,6 +2410,7 @@ customGames:
24022410
sort: Default::default(),
24032411
retention: Retention::default(),
24042412
format: Default::default(),
2413+
only_constructive: false,
24052414
},
24062415
restore: RestoreConfig {
24072416
path: StrictPath::new(s("~/restore")),

src/scan.rs

+19
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@ pub fn scan_game_for_backup(
512512
redirects: &[RedirectConfig],
513513
reverse_redirects_on_restore: bool,
514514
steam_shortcuts: &SteamShortcuts,
515+
only_constructive_backups: bool,
515516
) -> ScanInfo {
516517
log::trace!("[{name}] beginning scan for backup");
517518

@@ -876,6 +877,7 @@ pub fn scan_game_for_backup(
876877
backup: None,
877878
has_backups,
878879
dumped_registry,
880+
only_constructive_backups,
879881
}
880882
}
881883

@@ -957,6 +959,8 @@ mod tests {
957959
testing::{repo, s, EMPTY_HASH},
958960
};
959961

962+
const ONLY_CONSTRUCTIVE: bool = false;
963+
960964
fn config() -> Config {
961965
Config::load_from_string(&format!(
962966
r#"
@@ -1147,6 +1151,7 @@ mod tests {
11471151
&[],
11481152
false,
11491153
&Default::default(),
1154+
ONLY_CONSTRUCTIVE,
11501155
),
11511156
);
11521157

@@ -1173,6 +1178,7 @@ mod tests {
11731178
&[],
11741179
false,
11751180
&Default::default(),
1181+
ONLY_CONSTRUCTIVE,
11761182
),
11771183
);
11781184
}
@@ -1203,6 +1209,7 @@ mod tests {
12031209
&[],
12041210
false,
12051211
&Default::default(),
1212+
ONLY_CONSTRUCTIVE,
12061213
),
12071214
);
12081215
}
@@ -1245,6 +1252,7 @@ mod tests {
12451252
}],
12461253
false,
12471254
&Default::default(),
1255+
ONLY_CONSTRUCTIVE,
12481256
),
12491257
);
12501258
}
@@ -1275,6 +1283,7 @@ mod tests {
12751283
&[],
12761284
false,
12771285
&Default::default(),
1286+
ONLY_CONSTRUCTIVE,
12781287
),
12791288
);
12801289
}
@@ -1309,6 +1318,7 @@ mod tests {
13091318
&[],
13101319
false,
13111320
&Default::default(),
1321+
ONLY_CONSTRUCTIVE,
13121322
),
13131323
);
13141324
}
@@ -1342,6 +1352,7 @@ mod tests {
13421352
&[],
13431353
false,
13441354
&Default::default(),
1355+
ONLY_CONSTRUCTIVE,
13451356
),
13461357
);
13471358
}
@@ -1371,6 +1382,7 @@ mod tests {
13711382
&[],
13721383
false,
13731384
&Default::default(),
1385+
ONLY_CONSTRUCTIVE,
13741386
),
13751387
);
13761388
}
@@ -1400,6 +1412,7 @@ mod tests {
14001412
&[],
14011413
false,
14021414
&Default::default(),
1415+
ONLY_CONSTRUCTIVE,
14031416
),
14041417
);
14051418
}
@@ -1437,6 +1450,7 @@ mod tests {
14371450
&[],
14381451
false,
14391452
&Default::default(),
1453+
ONLY_CONSTRUCTIVE,
14401454
),
14411455
);
14421456
}
@@ -1476,6 +1490,7 @@ mod tests {
14761490
&[],
14771491
false,
14781492
&Default::default(),
1493+
ONLY_CONSTRUCTIVE,
14791494
),
14801495
);
14811496
}
@@ -1515,6 +1530,7 @@ mod tests {
15151530
&[],
15161531
false,
15171532
&Default::default(),
1533+
ONLY_CONSTRUCTIVE,
15181534
),
15191535
);
15201536
}
@@ -1563,6 +1579,7 @@ mod tests {
15631579
&[],
15641580
false,
15651581
&Default::default(),
1582+
ONLY_CONSTRUCTIVE,
15661583
),
15671584
);
15681585
}
@@ -1620,6 +1637,7 @@ mod tests {
16201637
&[],
16211638
false,
16221639
&Default::default(),
1640+
ONLY_CONSTRUCTIVE,
16231641
),
16241642
);
16251643
}
@@ -1750,6 +1768,7 @@ mod tests {
17501768
&[],
17511769
false,
17521770
&Default::default(),
1771+
ONLY_CONSTRUCTIVE,
17531772
),
17541773
);
17551774
}

src/scan/change.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,17 @@ impl ScanChangeCount {
120120
}
121121
}
122122

123-
pub fn overall(&self) -> ScanChange {
123+
pub fn overall(&self, only_constructive: bool) -> ScanChange {
124124
if self.brand_new() {
125125
ScanChange::New
126126
} else if self.only(ScanChange::Removed) {
127127
ScanChange::Removed
128128
} else if self.updated() {
129-
ScanChange::Different
129+
if only_constructive && self.new == 0 && self.different == 0 {
130+
ScanChange::Same
131+
} else {
132+
ScanChange::Different
133+
}
130134
} else if self.same != 0 {
131135
ScanChange::Same
132136
} else {

0 commit comments

Comments
 (0)