arch-pkg-text 0.10.0

Pure Rust library to parse Arch Linux packages' structured text formats
Documentation
use crate::{
    srcinfo::{
        field::FieldName,
        query::{QueryItem, QueryRawTextItem, Section},
    },
    value::Name,
};
use pipe_trait::Pipe;

macro_rules! def_cache {
    (
        base single ($($base_single_field:ident)*)
        base multi ($($base_multi_field:ident)*)
        shared single ($($shared_single_field:ident)*)
        shared multi no_arch ($($shared_multi_no_arch_field:ident)*)
        shared multi arch ($($shared_multi_arch_field:ident)*)
    ) => {
        #[derive(Debug, Default, Clone)]
        struct SingleValueCache<'a>(Option<&'a str>);

        impl<'a> SingleValueCache<'a> {
            fn get(&self, index: usize) -> Option<&'a str> {
                (index == 0).then_some(()).and(self.0)
            }

            fn add(&mut self, value: &'a str) {
                self.0 = Some(value);
            }

            fn add_opt(&mut self, value: Option<&'a str>) {
                if let Some(value) = value {
                    self.add(value);
                }
            }

            fn shrink_to_fit(&mut self) {}
        }

        #[derive(Debug, Clone)]
        struct MultiValueCache<Value>(Vec<Value>);

        impl<Value: Copy> MultiValueCache<Value> {
            fn get(&self, index: usize) -> Option<Value> {
                self.0.get(index).copied()
            }

            fn add(&mut self, value: Value) {
                self.0.push(value);
            }

            fn add_opt(&mut self, value: Option<Value>) {
                if let Some(value) = value {
                    self.add(value);
                }
            }

            fn shrink_to_fit(&mut self) {
                self.0.shrink_to_fit();
            }
        }

        impl<Value> Default for MultiValueCache<Value> {
            fn default() -> Self {
                MultiValueCache(Vec::new())
            }
        }

        #[derive(Debug, Default, Clone)]
        #[allow(non_snake_case, reason = "We don't access the field names directly, keep it simple.")]
        pub struct Cache<'a> {
            $($base_single_field: SingleValueCache<'a>,)*
            $($base_multi_field: MultiValueCache<&'a str>,)*
            $($shared_single_field: MultiValueCache<QueryItem<'a, &'a str, ()>>,)*
            $($shared_multi_no_arch_field: MultiValueCache<QueryItem<'a, &'a str, ()>>,)*
            $($shared_multi_arch_field: MultiValueCache<QueryRawTextItem<'a>>,)*
            derivative_names: MultiValueCache<&'a str>,
        }

        impl<'a> Cache<'a> {
            pub fn get(&self, field_name: FieldName, index: usize) -> Option<QueryRawTextItem<'a>> {
                match field_name {
                    $(FieldName::$base_single_field => self.$base_single_field.get(index).map(Cache::create_base_value),)*
                    $(FieldName::$base_multi_field => self.$base_multi_field.get(index).map(Cache::create_base_value),)*
                    $(FieldName::$shared_single_field => self.$shared_single_field.get(index).map(Cache::create_shared_value_no_arch),)*
                    $(FieldName::$shared_multi_no_arch_field => self.$shared_multi_no_arch_field.get(index).map(Cache::create_shared_value_no_arch),)*
                    $(FieldName::$shared_multi_arch_field => self.$shared_multi_arch_field.get(index),)*
                    FieldName::Name => self.derivative_names.get(index).map(Cache::create_name_value),
                }
            }

            fn create_base_value(value: &str) -> QueryRawTextItem<'_> {
                QueryRawTextItem::from_tuple3((value, Section::Base, None))
            }

            fn create_shared_value_no_arch(value: QueryItem<'a, &'a str, ()>) -> QueryRawTextItem<'a> {
                let (value, section) = value.into_tuple2();
                QueryRawTextItem::from_tuple3((value, section, None))
            }

            fn create_name_value(value: &str) -> QueryRawTextItem<'_> {
                let section = value.pipe(Name).pipe(Section::Derivative);
                QueryRawTextItem::from_tuple3((value, section, None))
            }

            pub fn add(&mut self, field_name: FieldName, item: QueryRawTextItem<'a>) {
                match field_name {
                    $(FieldName::$base_single_field => self.$base_single_field.add_opt(Cache::extract_base_value(item)),)*
                    $(FieldName::$base_multi_field => self.$base_multi_field.add_opt(Cache::extract_base_value(item)),)*
                    $(FieldName::$shared_single_field => self.$shared_single_field.add_opt(Cache::extract_shared_value_no_arch(item)),)*
                    $(FieldName::$shared_multi_no_arch_field => self.$shared_multi_no_arch_field.add_opt(Cache::extract_shared_value_no_arch(item)),)*
                    $(FieldName::$shared_multi_arch_field => self.$shared_multi_arch_field.add(item),)*
                    FieldName::Name => self.derivative_names.add_opt(Cache::extract_name_value(item)),
                }
            }

            fn extract_base_value(item: QueryRawTextItem<'_>) -> Option<&'_ str> {
                let (value, section, architecture) = item.into_tuple3();
                debug_assert_eq!(section, Section::Base);
                architecture.is_none().then_some(value)
            }

            fn extract_shared_value_no_arch(item: QueryRawTextItem<'_>) -> Option<QueryItem<'_, &'_ str, ()>> {
                let (value, section, architecture) = item.into_tuple3();
                architecture
                    .is_none()
                    .then_some(QueryItem::from_tuple2((value, section)))
            }

            fn extract_name_value(item: QueryRawTextItem<'_>) -> Option<&'_ str> {
                let (value, section, architecture) = item.into_tuple3();
                architecture.is_none().then_some(())?;
                debug_assert_eq!(section, value.pipe(Name).pipe(Section::Derivative));
                Some(value)
            }

            pub fn shrink_to_fit(&mut self) {
                $(self.$base_single_field.shrink_to_fit();)*
                $(self.$base_multi_field.shrink_to_fit();)*
                $(self.$shared_single_field.shrink_to_fit();)*
                $(self.$shared_multi_no_arch_field.shrink_to_fit();)*
                $(self.$shared_multi_arch_field.shrink_to_fit();)*
                self.derivative_names.shrink_to_fit();
            }
        }
    };
}

def_cache! {
    base single (Base Epoch Release Version)
    base multi (ValidPgpKeys)
    shared single (Description ChangeLog InstallScript Url)
    shared multi no_arch (Architecture Backup Groups License NoExtract Options)
    shared multi arch (
        Source Dependencies MakeDependencies CheckDependencies OptionalDependencies Provides Conflicts Replaces
        Md5Checksums Sha1Checksums Sha224Checksums Sha256Checksums Sha384Checksums Sha512Checksums Blake2bChecksums
    )
}