From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <pbs-devel-bounces@lists.proxmox.com>
Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9])
	by lore.proxmox.com (Postfix) with ESMTPS id B79991FF15C
	for <inbox@lore.proxmox.com>; Fri, 16 May 2025 13:54:55 +0200 (CEST)
Received: from firstgate.proxmox.com (localhost [127.0.0.1])
	by firstgate.proxmox.com (Proxmox) with ESMTP id 101B8C0BA;
	Fri, 16 May 2025 13:55:20 +0200 (CEST)
From: Christian Ebner <c.ebner@proxmox.com>
To: pbs-devel@lists.proxmox.com
Date: Fri, 16 May 2025 13:54:26 +0200
Message-Id: <20250516115429.208563-3-c.ebner@proxmox.com>
X-Mailer: git-send-email 2.39.5
In-Reply-To: <20250516115429.208563-1-c.ebner@proxmox.com>
References: <20250516115429.208563-1-c.ebner@proxmox.com>
MIME-Version: 1.0
X-SPAM-LEVEL: Spam detection results:  0
 AWL 0.029 Adjusted score from AWL reputation of From: address
 BAYES_00                 -1.9 Bayes spam probability is 0 to 1%
 DMARC_MISSING             0.1 Missing DMARC policy
 KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment
 SPF_HELO_NONE           0.001 SPF: HELO does not publish an SPF Record
 SPF_PASS               -0.001 SPF: sender matches SPF record
Subject: [pbs-devel] [PATCH proxmox-backup 2/5] api: factor out helper
 converting backup info to snapshot list item
X-BeenThere: pbs-devel@lists.proxmox.com
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: Proxmox Backup Server development discussion
 <pbs-devel.lists.proxmox.com>
List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pbs-devel>, 
 <mailto:pbs-devel-request@lists.proxmox.com?subject=unsubscribe>
List-Archive: <http://lists.proxmox.com/pipermail/pbs-devel/>
List-Post: <mailto:pbs-devel@lists.proxmox.com>
List-Help: <mailto:pbs-devel-request@lists.proxmox.com?subject=help>
List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel>, 
 <mailto:pbs-devel-request@lists.proxmox.com?subject=subscribe>
Reply-To: Proxmox Backup Server development discussion
 <pbs-devel@lists.proxmox.com>
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
Errors-To: pbs-devel-bounces@lists.proxmox.com
Sender: "pbs-devel" <pbs-devel-bounces@lists.proxmox.com>

Move the logic currently provided by a closure to it's dedicated
named helper function.

Further, in order to reuse the same code for the generation of the
snapshot list items to be used by the sync job source readers, move
the helper and related helper functions to the tools module.

No functional changes intended.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 src/api2/admin/datastore.rs | 135 +++---------------------------------
 src/tools/mod.rs            | 129 ++++++++++++++++++++++++++++++++++
 2 files changed, 137 insertions(+), 127 deletions(-)

diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs
index 55cb3a0b3..9b3ffb8e0 100644
--- a/src/api2/admin/datastore.rs
+++ b/src/api2/admin/datastore.rs
@@ -1,6 +1,5 @@
 //! Datastore Management
 
-use std::collections::HashSet;
 use std::ffi::OsStr;
 use std::ops::Deref;
 use std::os::unix::ffi::OsStrExt;
@@ -41,13 +40,12 @@ use pbs_api_types::{
     BackupContent, BackupGroupDeleteStats, BackupNamespace, BackupType, Counts, CryptMode,
     DataStoreConfig, DataStoreListItem, DataStoreMountStatus, DataStoreStatus,
     GarbageCollectionJobStatus, GroupListItem, JobScheduleStatus, KeepOptions, MaintenanceMode,
-    MaintenanceType, Operation, PruneJobOptions, SnapshotListItem, SnapshotVerifyState,
-    BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA,
-    BACKUP_TYPE_SCHEMA, CATALOG_NAME, CLIENT_LOG_BLOB_NAME, DATASTORE_SCHEMA,
-    IGNORE_VERIFIED_BACKUPS_SCHEMA, MANIFEST_BLOB_NAME, MAX_NAMESPACE_DEPTH, NS_MAX_DEPTH_SCHEMA,
-    PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE,
-    PRIV_DATASTORE_READ, PRIV_DATASTORE_VERIFY, PRIV_SYS_MODIFY, UPID, UPID_SCHEMA,
-    VERIFICATION_OUTDATED_AFTER_SCHEMA,
+    MaintenanceType, Operation, PruneJobOptions, SnapshotListItem, BACKUP_ARCHIVE_NAME_SCHEMA,
+    BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA,
+    CATALOG_NAME, CLIENT_LOG_BLOB_NAME, DATASTORE_SCHEMA, IGNORE_VERIFIED_BACKUPS_SCHEMA,
+    MAX_NAMESPACE_DEPTH, NS_MAX_DEPTH_SCHEMA, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP,
+    PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE, PRIV_DATASTORE_READ, PRIV_DATASTORE_VERIFY,
+    PRIV_SYS_MODIFY, UPID, UPID_SCHEMA, VERIFICATION_OUTDATED_AFTER_SCHEMA,
 };
 use pbs_client::pxar::{create_tar, create_zip};
 use pbs_config::CachedUserInfo;
@@ -74,8 +72,8 @@ use crate::backup::{
     check_ns_privs_full, verify_all_backups, verify_backup_dir, verify_backup_group, verify_filter,
     ListAccessibleBackupGroups, NS_PRIVS_OK,
 };
-
 use crate::server::jobstate::{compute_schedule_status, Job, JobState};
+use crate::tools::{backup_info_to_snapshot_list_item, get_all_snapshot_files, read_backup_index};
 
 const GROUP_NOTES_FILE_NAME: &str = "notes";
 
@@ -114,56 +112,6 @@ fn check_privs_and_load_store(
     Ok(datastore)
 }
 
-fn read_backup_index(
-    backup_dir: &BackupDir,
-) -> Result<(BackupManifest, Vec<BackupContent>), Error> {
-    let (manifest, index_size) = backup_dir.load_manifest()?;
-
-    let mut result = Vec::new();
-    for item in manifest.files() {
-        result.push(BackupContent {
-            filename: item.filename.clone(),
-            crypt_mode: Some(item.crypt_mode),
-            size: Some(item.size),
-        });
-    }
-
-    result.push(BackupContent {
-        filename: MANIFEST_BLOB_NAME.to_string(),
-        crypt_mode: match manifest.signature {
-            Some(_) => Some(CryptMode::SignOnly),
-            None => Some(CryptMode::None),
-        },
-        size: Some(index_size),
-    });
-
-    Ok((manifest, result))
-}
-
-fn get_all_snapshot_files(
-    info: &BackupInfo,
-) -> Result<(BackupManifest, Vec<BackupContent>), Error> {
-    let (manifest, mut files) = read_backup_index(&info.backup_dir)?;
-
-    let file_set = files.iter().fold(HashSet::new(), |mut acc, item| {
-        acc.insert(item.filename.clone());
-        acc
-    });
-
-    for file in &info.files {
-        if file_set.contains(file) {
-            continue;
-        }
-        files.push(BackupContent {
-            filename: file.to_string(),
-            size: None,
-            crypt_mode: None,
-        });
-    }
-
-    Ok((manifest, files))
-}
-
 #[api(
     input: {
         properties: {
@@ -529,73 +477,6 @@ unsafe fn list_snapshots_blocking(
         (None, None) => datastore.list_backup_groups(ns.clone())?,
     };
 
-    let info_to_snapshot_list_item = |owner, info: BackupInfo| {
-        let backup = info.backup_dir.dir().to_owned();
-        let protected = info.protected;
-
-        match get_all_snapshot_files(&info) {
-            Ok((manifest, files)) => {
-                // extract the first line from notes
-                let comment: Option<String> = manifest.unprotected["notes"]
-                    .as_str()
-                    .and_then(|notes| notes.lines().next())
-                    .map(String::from);
-
-                let fingerprint = match manifest.fingerprint() {
-                    Ok(fp) => fp,
-                    Err(err) => {
-                        eprintln!("error parsing fingerprint: '{}'", err);
-                        None
-                    }
-                };
-
-                let verification: Option<SnapshotVerifyState> = match manifest.verify_state() {
-                    Ok(verify) => verify,
-                    Err(err) => {
-                        eprintln!("error parsing verification state : '{}'", err);
-                        None
-                    }
-                };
-
-                let size = Some(files.iter().map(|x| x.size.unwrap_or(0)).sum());
-
-                SnapshotListItem {
-                    backup,
-                    comment,
-                    verification,
-                    fingerprint,
-                    files,
-                    size,
-                    owner,
-                    protected,
-                }
-            }
-            Err(err) => {
-                eprintln!("error during snapshot file listing: '{}'", err);
-                let files = info
-                    .files
-                    .into_iter()
-                    .map(|filename| BackupContent {
-                        filename,
-                        size: None,
-                        crypt_mode: None,
-                    })
-                    .collect();
-
-                SnapshotListItem {
-                    backup,
-                    comment: None,
-                    verification: None,
-                    fingerprint: None,
-                    files,
-                    size: None,
-                    owner,
-                    protected,
-                }
-            }
-        }
-    };
-
     groups.iter().try_fold(Vec::new(), |mut snapshots, group| {
         let owner = match group.get_owner() {
             Ok(auth_id) => auth_id,
@@ -619,7 +500,7 @@ unsafe fn list_snapshots_blocking(
         snapshots.extend(
             group_backups
                 .into_iter()
-                .map(|info| info_to_snapshot_list_item(Some(owner.clone()), info)),
+                .map(|info| backup_info_to_snapshot_list_item(&info, &owner)),
         );
 
         Ok(snapshots)
diff --git a/src/tools/mod.rs b/src/tools/mod.rs
index 322894dd7..6556effe3 100644
--- a/src/tools/mod.rs
+++ b/src/tools/mod.rs
@@ -3,9 +3,16 @@
 //! This is a collection of small and useful tools.
 
 use anyhow::{bail, Error};
+use std::collections::HashSet;
 
+use pbs_api_types::{
+    Authid, BackupContent, CryptMode, SnapshotListItem, SnapshotVerifyState, MANIFEST_BLOB_NAME,
+};
 use proxmox_http::{client::Client, HttpOptions, ProxyConfig};
 
+use pbs_datastore::backup_info::{BackupDir, BackupInfo};
+use pbs_datastore::manifest::BackupManifest;
+
 pub mod config;
 pub mod disks;
 pub mod fs;
@@ -61,3 +68,125 @@ pub fn setup_safe_path_env() {
         std::env::remove_var(name);
     }
 }
+
+pub(crate) fn read_backup_index(
+    backup_dir: &BackupDir,
+) -> Result<(BackupManifest, Vec<BackupContent>), Error> {
+    let (manifest, index_size) = backup_dir.load_manifest()?;
+
+    let mut result = Vec::new();
+    for item in manifest.files() {
+        result.push(BackupContent {
+            filename: item.filename.clone(),
+            crypt_mode: Some(item.crypt_mode),
+            size: Some(item.size),
+        });
+    }
+
+    result.push(BackupContent {
+        filename: MANIFEST_BLOB_NAME.to_string(),
+        crypt_mode: match manifest.signature {
+            Some(_) => Some(CryptMode::SignOnly),
+            None => Some(CryptMode::None),
+        },
+        size: Some(index_size),
+    });
+
+    Ok((manifest, result))
+}
+
+pub(crate) fn get_all_snapshot_files(
+    info: &BackupInfo,
+) -> Result<(BackupManifest, Vec<BackupContent>), Error> {
+    let (manifest, mut files) = read_backup_index(&info.backup_dir)?;
+
+    let file_set = files.iter().fold(HashSet::new(), |mut acc, item| {
+        acc.insert(item.filename.clone());
+        acc
+    });
+
+    for file in &info.files {
+        if file_set.contains(file) {
+            continue;
+        }
+        files.push(BackupContent {
+            filename: file.to_string(),
+            size: None,
+            crypt_mode: None,
+        });
+    }
+
+    Ok((manifest, files))
+}
+
+/// Helper to transform `BackupInfo` to `SnapshotListItem` with given owner.
+pub(crate) fn backup_info_to_snapshot_list_item(
+    info: &BackupInfo,
+    owner: &Authid,
+) -> SnapshotListItem {
+    let backup = info.backup_dir.dir().to_owned();
+    let protected = info.protected;
+    let owner = Some(owner.to_owned());
+
+    match get_all_snapshot_files(info) {
+        Ok((manifest, files)) => {
+            // extract the first line from notes
+            let comment: Option<String> = manifest.unprotected["notes"]
+                .as_str()
+                .and_then(|notes| notes.lines().next())
+                .map(String::from);
+
+            let fingerprint = match manifest.fingerprint() {
+                Ok(fp) => fp,
+                Err(err) => {
+                    eprintln!("error parsing fingerprint: '{}'", err);
+                    None
+                }
+            };
+
+            let verification: Option<SnapshotVerifyState> = match manifest.verify_state() {
+                Ok(verify) => verify,
+                Err(err) => {
+                    eprintln!("error parsing verification state : '{err}'");
+                    None
+                }
+            };
+
+            let size = Some(files.iter().map(|x| x.size.unwrap_or(0)).sum());
+
+            SnapshotListItem {
+                backup,
+                comment,
+                verification,
+                fingerprint,
+                files,
+                size,
+                owner,
+                protected,
+            }
+        }
+        Err(err) => {
+            eprintln!("error during snapshot file listing: '{err}'");
+            let files = info
+                .files
+                .iter()
+                .map(|filename| BackupContent {
+                    filename: filename.to_owned(),
+                    size: None,
+                    crypt_mode: None,
+                })
+                .collect();
+
+            SnapshotListItem {
+                backup,
+                comment: None,
+                verification: None,
+                fingerprint: None,
+                files,
+                size: None,
+                owner,
+                protected,
+            }
+        }
+    }
+}
-- 
2.39.5



_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel