[pbs-devel] [PATCH v3 proxmox-backup 2/5] api types: introduce `BackupArchiveName` type
Christian Ebner
c.ebner at proxmox.com
Mon Nov 4 12:56:14 CET 2024
On 10/25/24 14:15, Fabian Grünbichler wrote:
> On October 24, 2024 10:01 am, Christian Ebner wrote:
>> Introduces a dedicated wrapper type to be used for backup archive
>> names instead of plain strings and associated helper methods for
>> archive type checks and archive name mappings.
>>
>> Signed-off-by: Christian Ebner <c.ebner at proxmox.com>
>> ---
>> changes since version 2:
>> - reworded commit message
>>
>> pbs-api-types/src/datastore.rs | 107 ++++++++++++++++++++++++++++++++-
>> 1 file changed, 106 insertions(+), 1 deletion(-)
>>
>> diff --git a/pbs-api-types/src/datastore.rs b/pbs-api-types/src/datastore.rs
>> index dfa6bb259..62e4f7345 100644
>> --- a/pbs-api-types/src/datastore.rs
>> +++ b/pbs-api-types/src/datastore.rs
>> @@ -1,5 +1,7 @@
>> +use std::convert::{AsRef, TryFrom};
>> use std::fmt;
>> use std::path::{Path, PathBuf};
>> +use std::str::FromStr;
>>
>> use anyhow::{bail, format_err, Error};
>> use const_format::concatcp;
>> @@ -1570,7 +1572,7 @@ pub fn print_store_and_ns(store: &str, ns: &BackupNamespace) -> String {
>> }
>> }
>>
>> -#[derive(PartialEq, Eq)]
>> +#[derive(Clone, PartialEq, Eq)]
>> /// Allowed variants of backup archives to be contained in a snapshot's manifest
>> pub enum ArchiveType {
>> FixedIndex,
>> @@ -1590,3 +1592,106 @@ impl ArchiveType {
>> Ok(archive_type)
>> }
>> }
>> +
>> +#[derive(Clone, PartialEq, Eq)]
>> +/// Name of archive files contained in snapshot's manifest
>> +pub struct BackupArchiveName {
>> + // archive name including the `.fidx`, `.didx` or `.blob` extension
>> + name: String,
>> + // type parsed based on given extension
>> + ty: ArchiveType,
>> +}
>> +
>> +impl fmt::Display for BackupArchiveName {
>> + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
>> + write!(f, "{name}", name = self.name)
>> + }
>> +}
>> +
>> +serde_plain::derive_deserialize_from_fromstr!(BackupArchiveName, "archive name");
>> +
>> +impl FromStr for BackupArchiveName {
>> + type Err = Error;
>> +
>> + fn from_str(name: &str) -> Result<Self, Self::Err> {
>> + Self::try_from(name)
>> + }
>> +}
>> +
>> +serde_plain::derive_serialize_from_display!(BackupArchiveName);
>> +
>> +impl TryFrom<&str> for BackupArchiveName {
>> + type Error = anyhow::Error;
>> +
>> + fn try_from(value: &str) -> Result<Self, Self::Error> {
>> + let (name, ty) = Self::parse_archive_type(value)?;
>> + Ok(Self { name, ty })
>> + }
>> +}
>> +
>> +impl AsRef<str> for BackupArchiveName {
>> + fn as_ref(&self) -> &str {
>> + &self.name
>> + }
>> +}
>> +
>> +impl BackupArchiveName {
>> + pub fn from_path(path: impl AsRef<Path>) -> Result<Self, Error> {
>> + let path = path.as_ref();
>> + if path.as_os_str().as_encoded_bytes().last() == Some(&b'/') {
>> + bail!("invalid archive name, got directory");
>> + }
>> + let file_name = path
>> + .file_name()
>> + .ok_or_else(|| format_err!("invalid archive name"))?;
>> + let name = file_name
>> + .to_str()
>> + .ok_or_else(|| format_err!("archive name not valid UTF-8"))?;
>> +
>> + Self::try_from(name)
>> + }
>> +
>> + pub fn archive_type(&self) -> ArchiveType {
>> + self.ty.clone()
>> + }
>> +
>> + pub fn ends_with(&self, postfix: &str) -> bool {
>> + self.name.ends_with(postfix)
>> + }
>> +
>> + pub fn has_pxar_filename_extension(&self) -> bool {
>> + self.name.ends_with(".pxar.didx")
>> + || self.name.ends_with(".mpxar.didx")
>> + || self.name.ends_with(".ppxar.didx")
>> + }
>> +
>> + pub fn without_type_extension(&self) -> String {
>> + match self.ty {
>> + ArchiveType::DynamicIndex => self.name.strip_suffix(".didx").unwrap().into(),
>> + ArchiveType::FixedIndex => self.name.strip_suffix(".fidx").unwrap().into(),
>> + ArchiveType::Blob => self.name.strip_suffix(".blob").unwrap().into(),
>
> if ArchiveType would have a getter for the corresponding extension, then
> this could just become
>
> self.name.strip_suffix(self.ty.extension()).unwrap().into()
Acked, will adapt for version 4 of the patch series.
>
>> + }
>> + }
>> +
>> + fn parse_archive_type(archive_name: &str) -> Result<(String, ArchiveType), Error> {
>> + if archive_name.ends_with(".didx")
>> + || archive_name.ends_with(".fidx")
>> + || archive_name.ends_with(".blob")
>> + {
>> + Ok((archive_name.into(), ArchiveType::from_path(archive_name)?))
>
> and this here could maybe also be turned around -> get the extension
> from archive_name:
>
> if let Ok(ty) = ArchiveType::from_path(..) {
> Ok((archive_name.into(), ty))
> } else if ..
Agreed, will also incorporate this.
>
>> + } else if archive_name.ends_with(".pxar")
>> + || archive_name.ends_with(".mpxar")
>> + || archive_name.ends_with(".ppxar")
>> + {
>> + Ok((format!("{archive_name}.didx"), ArchiveType::DynamicIndex))
>> + } else if archive_name.ends_with(".img") {
>> + Ok((format!("{archive_name}.fidx"), ArchiveType::FixedIndex))
>
> not sure whether we want these associations (between ArchiveType and
> contained files) to live somewhere more declarative?
Could be moved to the `ArchiveType`, as `archive_extension` mimicking
the `extension` getter as suggested above.
>
>> + } else {
>> + Ok((format!("{archive_name}.blob"), ArchiveType::Blob))
>
> this last catchall here might be a bit dangerous? it basically makes the
> introduction of a new archive type collide with any existing blobs that
> happen to have a file name that ends with that new archive type..
This is true, but we already have that exact same mapping currently in
use (see patch 4, which drops the pre-existing helper). But that was
arguably more limited in scope.
So maybe we might keep the pre-existing helper here instead of
implementing `parse_archive_type` for the API type directly?
>
> e.g., qemu-server.conf.blob
>
> with this patch, qemu-server.conf will automatically expand to
> qemu-server.conf.blob
>
> if we then at some point introduce an archive type `conf`, then it
> wouldn't anymore, but instead be interpreted as archive of that type..
>
> similarly, all of the above "inner" extensions are burned for archive
> type usage already..
>
> this was basically the only reason I didn't apply this right now leaving
> the rest above as follow-up material ;)
>
>> + }
>> + }
>> +}
>> +
>> +impl ApiType for BackupArchiveName {
>> + const API_SCHEMA: Schema = BACKUP_ARCHIVE_NAME_SCHEMA;
>> +}
>> --
>> 2.39.5
>>
>>
>>
>> _______________________________________________
>> pbs-devel mailing list
>> pbs-devel at lists.proxmox.com
>> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
>>
>>
>>
>
>
> _______________________________________________
> pbs-devel mailing list
> pbs-devel at lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
>
>
More information about the pbs-devel
mailing list