Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 400b0849 authored by William Escande's avatar William Escande Committed by Rahul Arya
Browse files

GD Rust: init_flag add remaining flags types

Add custom type support in the macro and add a int and custom type flag.
Add some new test in rust

Test: cargo test init_flags
Bug: 251328572
Tag: #refactor
Merged-In: I0244c598dd4ca051d79d260656632d53026f22e7
Change-Id: I0244c598dd4ca051d79d260656632d53026f22e7
(cherry picked from commit 40cddaef)
parent aea14522
Loading
Loading
Loading
Loading
+184 −36
Original line number Diff line number Diff line
use log::{error, info, warn};
use log::{error, info};
use paste::paste;
use std::collections::HashMap;
use std::fmt;
use std::sync::Mutex;

// Fallback to bool when type is not specified
macro_rules! type_expand {
    () => {
        bool
    };
    ($type:ty) => {
        $type
    };
}

macro_rules! default_value {
    () => {
        false
    };
    ($default:tt) => {
    ($type:ty) => {
        <$type>::default()
    };
    ($($type:ty)? = $default:tt) => {
        $default
    };
}

macro_rules! default_flag {
macro_rules! test_value {
    () => {
        true
    };
    ($type:ty) => {
        <$type>::default()
    };
}

macro_rules! create_getter_fn {
    ($flag:ident) => {
        let $flag = false;
        paste! {
            #[doc = concat!(" Return true if ", stringify!($flag), " is enabled")]
            pub fn [<$flag _is_enabled>]() -> bool {
                FLAGS.lock().unwrap().$flag
            }
        }
    };
    ($flag:ident = $default:tt) => {
        let $flag = $default;
    ($flag:ident $type:ty) => {
        paste! {
            #[doc = concat!(" Return the flag value of ", stringify!($flag))]
            pub fn [<get_ $flag>]() -> $type {
                FLAGS.lock().unwrap().$flag
            }
        }
    };
}

macro_rules! init_flags {
    (flags: { $($flag:ident $(= $default:tt)?,)* }, dependencies: { $($parent:ident => $child:ident),* }) => {
    (flags: { $($flag:ident $(: $type:ty)? $(= $default:tt)?,)* }
     extra_fields: { $($extra_field:ident : $extra_field_type:ty $(= $extra_default:tt)?,)* }
     extra_parsed_flags: { $($extra_flag:tt => $extra_flag_fn:ident(_, _ $(,$extra_args:tt)*),)*}
     dependencies: { $($parent:ident => $child:ident),* }) => {

        struct InitFlags {
            $($flag: bool,)*
            $($flag : type_expand!($($type)?),)*
            $($extra_field : $extra_field_type,)*
        }

        impl Default for InitFlags {
            fn default() -> Self {
                $(default_flag!($flag $(= $default)?);)*
                Self { $($flag,)* }
                Self {
                    $($flag : default_value!($($type)? $(= $default)?),)*
                    $($extra_field : default_value!($extra_field_type $(= $extra_default)?),)*
                }
            }
        }

        /// Sets all flags to true, for testing
        /// Sets all bool flags to true
        /// Set all other flags and extra fields to their default type value
        pub fn set_all_for_testing() {
            *FLAGS.lock().unwrap() = InitFlags { $($flag: true,)* };
            *FLAGS.lock().unwrap() = InitFlags {
                $($flag: test_value!($($type)?),)*
                $($extra_field: test_value!($extra_field_type),)*
            };
        }

        impl InitFlags {
@@ -51,8 +96,9 @@ macro_rules! init_flags {

                    match values[0] {
                        $(concat!("INIT_", stringify!($flag)) =>
                            init_flags.$flag = values[1].parse().unwrap_or(default_value!($($default)?)),)*
                        _ => warn!("Unsaved flag: {} = {}", values[0], values[1])
                            init_flags.$flag = values[1].parse().unwrap_or(default_value!($($type)? $(= $default)?)),)*
                        $($extra_flag => $extra_flag_fn(&mut init_flags, values $(, $extra_args)*),)*
                        _ => error!("Unsaved flag: {} = {}", values[0], values[1])
                    }
                }

@@ -60,18 +106,14 @@ macro_rules! init_flags {
            }

            fn reconcile(mut self) -> Self {
                // Loop to ensure dependencies can be specified in any order
                loop {
                    let mut any_change = false;
                    // dependencies can be specified in any order
                    $(if self.$parent && !self.$child {
                        self.$child = true;
                        any_change = true;
                        continue;
                    })*

                    if !any_change {
                    break;
                }
                }

                // TODO: acl should not be off if l2cap is on, but need to reconcile legacy code
                if self.gd_l2cap {
@@ -80,21 +122,51 @@ macro_rules! init_flags {

                self
            }
        }

            fn log(&self) {
                info!(concat!("Flags loaded: ", $(stringify!($flag), "={} ",)*), $(self.$flag,)*);
        impl fmt::Display for InitFlags {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, concat!(
                    concat!($(concat!(stringify!($flag), "={}")),*),
                    $(concat!(stringify!($extra_field), "={}")),*),
                    $(self.$flag),*,
                    $(self.$extra_field),*)
            }
        }

        paste! {
            $(
                #[allow(missing_docs)]
                pub fn [<$flag _is_enabled>]() -> bool {
                    FLAGS.lock().unwrap().$flag
        $(create_getter_fn!($flag $($type)?);)*
    }
            )*
}
    };

#[derive(Default)]
struct ExplicitTagSettings {
    map: HashMap<String, bool>,
}

impl fmt::Display for ExplicitTagSettings {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.map)
    }
}

fn parse_logging_tag(flags: &mut InitFlags, values: Vec<&str>, enabled: bool) {
    for tag in values[1].split(',') {
        flags.logging_debug_explicit_tag_settings.map.insert(tag.to_string(), enabled);
    }
}

/// Return true if `tag` is enabled in the flag
pub fn is_debug_logging_enabled_for_tag(tag: &str) -> bool {
    let guard = FLAGS.lock().unwrap();
    *guard
        .logging_debug_explicit_tag_settings
        .map
        .get(tag)
        .unwrap_or(&guard.logging_debug_enabled_for_all)
}

fn parse_hci_adapter(flags: &mut InitFlags, values: Vec<&str>) {
    flags.hci_adapter = values[1].parse().unwrap_or(0);
}

init_flags!(
@@ -108,11 +180,22 @@ init_flags!(
        gd_link_policy,
        gd_rust,
        gd_security,
        hci_adapter: i32,
        irk_rotation,
        logging_debug_enabled_for_all,
        pass_phy_update_callback = true,
        sdp_serialization = true,
        sdp_skip_rnr_if_known = true,
    },
    }
    // extra_fields are not a 1 to 1 match with "INIT_*" flags
    extra_fields: {
        logging_debug_explicit_tag_settings: ExplicitTagSettings,
    }
    extra_parsed_flags: {
        "INIT_logging_debug_enabled_for_tags" => parse_logging_tag(_, _, true),
        "INIT_logging_debug_disabled_for_tags" => parse_logging_tag(_, _, false),
        "--hci" => parse_hci_adapter(_, _),
    }
    dependencies: {
        gd_core => gd_security
    }
@@ -123,10 +206,75 @@ lazy_static! {
}

/// Loads the flag values from the passed-in vector of string values
pub fn load(flags: Vec<String>) {
pub fn load(raw_flags: Vec<String>) {
    crate::init_logging();

    let flags = InitFlags::parse(flags);
    flags.log();
    let flags = InitFlags::parse(raw_flags);
    info!("Flags loaded: {}", flags);
    *FLAGS.lock().unwrap() = flags;
}

#[cfg(test)]
mod tests {
    use super::*;
    lazy_static! {
        /// do not run concurrent tests as they all use the same global init_flag struct and
        /// accessor
        static ref ASYNC_LOCK: Mutex<()> = Mutex::new(());
    }

    fn test_load(raw_flags: Vec<&str>) {
        let raw_flags = raw_flags.into_iter().map(|x| x.to_string()).collect();
        load(raw_flags);
    }

    #[test]
    fn simple_flag() {
        let _guard = ASYNC_LOCK.lock().unwrap();
        test_load(vec![
            "INIT_btaa_hci=false", //override a default flag
            "INIT_gatt_robust_caching_server=true",
        ]);
        assert!(!btaa_hci_is_enabled());
        assert!(gatt_robust_caching_server_is_enabled());
    }
    #[test]
    fn parsing_failure() {
        let _guard = ASYNC_LOCK.lock().unwrap();
        test_load(vec![
            "foo=bar=?",                                // vec length
            "foo=bar",                                  // flag not save
            "INIT_btaa_hci=not_false",                  // parse error but has default value
            "INIT_gatt_robust_caching_server=not_true", // parse error
        ]);
        assert!(btaa_hci_is_enabled());
        assert!(!gatt_robust_caching_server_is_enabled());
    }
    #[test]
    fn int_flag() {
        let _guard = ASYNC_LOCK.lock().unwrap();
        test_load(vec!["--hci=2"]);
        assert_eq!(get_hci_adapter(), 2);
    }
    #[test]
    fn explicit_flag() {
        let _guard = ASYNC_LOCK.lock().unwrap();
        test_load(vec![
            "INIT_logging_debug_enabled_for_all=true",
            "INIT_logging_debug_enabled_for_tags=foo,bar",
            "INIT_logging_debug_disabled_for_tags=foo,bar2",
            "INIT_logging_debug_enabled_for_tags=bar2",
        ]);
        assert!(!is_debug_logging_enabled_for_tag("foo"));
        assert!(is_debug_logging_enabled_for_tag("bar"));
        assert!(is_debug_logging_enabled_for_tag("bar2"));
        assert!(is_debug_logging_enabled_for_tag("unknown_flag"));
        assert!(logging_debug_enabled_for_all_is_enabled());
        FLAGS.lock().unwrap().logging_debug_enabled_for_all = false;
        assert!(!is_debug_logging_enabled_for_tag("foo"));
        assert!(is_debug_logging_enabled_for_tag("bar"));
        assert!(is_debug_logging_enabled_for_tag("bar2"));
        assert!(!is_debug_logging_enabled_for_tag("unknown_flag"));
        assert!(!logging_debug_enabled_for_all_is_enabled());
    }
}