Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/build/build/make/tools/aconfig/aconfig/src/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 18 kB image not shown  

Quelle  dump.rs

  Sprache: Rust
 

/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


use aconfig_protos::{
    ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagPurpose, ProtoFlagState,
    ProtoFlagStorageBackend, ProtoTracepoint,
};
use aconfig_protos::{ProtoParsedFlag, ProtoParsedFlags};
use anyhow::{anyhow, bail, Context, Result};
use protobuf::Message;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DumpFormat {
    Protobuf,
    Textproto,
    Custom(String),
}

impl TryFrom<&str> for DumpFormat {
    type Error = anyhow::Error;

    fn try_from(value: &str) -> std::result::Result<SelfSelf::Error> {
        match value {
            // protobuf formats
            "protobuf" => Ok(Self::Protobuf),
            "textproto" => Ok(Self::Textproto),
            // custom format
            _ => Ok(Self::Custom(value.to_owned())),
        }
    }
}

pub fn dump_parsed_flags<I>(parsed_flags_iter: I, format: DumpFormat) -> Result<Vec<u8>>
where
    I: Iterator<Item = ProtoParsedFlag>,
{
    let mut output = Vec::new();
    match format {
        DumpFormat::Protobuf => {
            let parsed_flags =
                ProtoParsedFlags { parsed_flag: parsed_flags_iter.collect(), ..Default::default() };
            parsed_flags.write_to_vec(&mut output)?;
        }
        DumpFormat::Textproto => {
            let parsed_flags =
                ProtoParsedFlags { parsed_flag: parsed_flags_iter.collect(), ..Default::default() };
            let s = protobuf::text_format::print_to_string_pretty(&parsed_flags);
            output.extend_from_slice(s.as_bytes());
        }
        DumpFormat::Custom(format) => {
            for flag in parsed_flags_iter {
                dump_custom_format(&flag, &format, &mut output);
            }
        }
    }
    Ok(output)
}

fn dump_custom_format(flag: &ProtoParsedFlag, format: &str, output: &mut Vec<u8>) {
    fn format_trace(trace: &[ProtoTracepoint]) -> String {
        trace
            .iter()
            .map(|tracepoint| {
                format!(
                    "{}: {:?} + {:?}",
                    tracepoint.source(),
                    tracepoint.permission(),
                    tracepoint.state()
                )
            })
            .collect::<Vec<_>>()
            .join(", ")
    }

    fn format_trace_paths(trace: &[ProtoTracepoint]) -> String {
        trace.iter().map(|tracepoint| tracepoint.source()).collect::<Vec<_>>().join(", ")
    }

    fn format_metadata_purpose(metadata: &ProtoFlagMetadata) -> String {
        format!("{:?}", metadata.purpose())
    }

    fn format_metadata_storage(metadata: &ProtoFlagMetadata) -> String {
        format!("{:?}", metadata.storage())
    }

    let mut str = format
        // ProtoParsedFlag fields
        .replace("{package}", flag.package())
        .replace("{name}", flag.name())
        .replace("{namespace}", flag.namespace())
        .replace("{description}", flag.description())
        .replace("{bug}", &flag.bug.join(", "))
        .replace("{state}", &format!("{:?}", flag.state()))
        .replace("{state:bool}", &format!("{}", flag.state() == ProtoFlagState::ENABLED))
        .replace("{permission}", &format!("{:?}", flag.permission()))
        .replace("{trace}", &format_trace(&flag.trace))
        .replace("{trace:paths}", &format_trace_paths(&flag.trace))
        .replace("{is_fixed_read_only}", &format!("{}", flag.is_fixed_read_only()))
        .replace("{is_exported}", &format!("{}", flag.is_exported()))
        .replace("{container}", flag.container())
        .replace("{metadata.purpose}", &format_metadata_purpose(&flag.metadata))
        .replace("{metadata.storage}", &format_metadata_storage(&flag.metadata))
        // ParsedFlagExt functions
        .replace("{fully_qualified_name}", &flag.fully_qualified_name());
    str.push('\n');
    output.extend_from_slice(str.as_bytes());
}

pub type DumpPredicate = dyn Fn(&ProtoParsedFlag) -> bool;

pub fn create_filter_predicate(filter: &str) -> Result<Box<DumpPredicate>> {
    let predicates = filter
        .split('+')
        .map(|sub_filter| create_filter_predicate_single(sub_filter))
        .collect::<Result<Vec<_>>>()?;
    Ok(Box::new(move |flag| predicates.iter().all(|p| p(flag))))
}

fn create_filter_predicate_single(filter: &str) -> Result<Box<DumpPredicate>> {
    fn enum_from_str<T>(expected: &[T], s: &str) -> Result<T>
    where
        T: std::fmt::Debug + Copy,
    {
        for candidate in expected.iter() {
            if s == format!("{candidate:?}") {
                return Ok(*candidate);
            }
        }
        let expected =
            expected.iter().map(|state| format!("{state:?}")).collect::<Vec<_>>().join(", ");
        bail!("\"{s}\": not a valid flag state, expected one of {expected}");
    }

    let error_msg = format!("\"{filter}\": filter syntax error");
    let (what, arg) = filter.split_once(':').ok_or_else(|| anyhow!(error_msg.clone()))?;
    match what {
        "package" => {
            let expected = arg.to_owned();
            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.package() == expected))
        }
        "name" => {
            let expected = arg.to_owned();
            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.name() == expected))
        }
        "namespace" => {
            if let Some(arg_val) = arg.strip_prefix("!") {
                let expected = arg_val.to_owned();
                Ok(Box::new(move |flag: &ProtoParsedFlag| flag.namespace() != expected))
            } else {
                let expected = arg.to_owned();
                Ok(Box::new(move |flag: &ProtoParsedFlag| flag.namespace() == expected))
            }
        }
        // description: not supported yet
        "bug" => {
            let expected = arg.to_owned();
            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.bug.join(", ") == expected))
        }
        "state" => {
            let expected = enum_from_str(&[ProtoFlagState::ENABLED, ProtoFlagState::DISABLED], arg)
                .context(error_msg)?;
            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.state() == expected))
        }
        "permission" => {
            let expected = enum_from_str(
                &[ProtoFlagPermission::READ_ONLY, ProtoFlagPermission::READ_WRITE],
                arg,
            )
            .context(error_msg)?;
            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.permission() == expected))
        }
        // trace: not supported yet
        "is_fixed_read_only" => {
            let expected: bool = arg.parse().context(error_msg)?;
            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.is_fixed_read_only() == expected))
        }
        "is_exported" => {
            let expected: bool = arg.parse().context(error_msg)?;
            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.is_exported() == expected))
        }
        "container" => {
            let expected = arg.to_owned();
            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.container() == expected))
        }
        "metadata.purpose" => {
            let expected = enum_from_str(
                &[
                    ProtoFlagPurpose::PURPOSE_UNSPECIFIED,
                    ProtoFlagPurpose::PURPOSE_FEATURE,
                    ProtoFlagPurpose::PURPOSE_BUGFIX,
                ],
                arg,
            )
            .context(error_msg)?;
            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.metadata.purpose() == expected))
        }
        "metadata.storage" => {
            let expected = enum_from_str(
                &[
                    ProtoFlagStorageBackend::UNSPECIFIED,
                    ProtoFlagStorageBackend::ACONFIGD,
                    ProtoFlagStorageBackend::DEVICE_CONFIG,
                    ProtoFlagStorageBackend::NONE,
                ],
                arg,
            )
            .context(error_msg)?;
            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.metadata.storage() == expected))
        }
        "fully_qualified_name" => {
            let expected = arg.to_owned();
            Ok(Box::new(move |flag: &ProtoParsedFlag| flag.fully_qualified_name() == expected))
        }
        _ => Err(anyhow!(error_msg)),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test::parse_test_flags;
    use aconfig_protos::ProtoParsedFlags;
    use protobuf::Message;

    fn parse_enabled_ro_flag() -> ProtoParsedFlag {
        parse_test_flags().parsed_flag.into_iter().find(|pf| pf.name() == "enabled_ro").unwrap()
    }

    #[test]
    fn test_dumpformat_from_str() {
        // supported format types
        assert_eq!(DumpFormat::try_from("protobuf").unwrap(), DumpFormat::Protobuf);
        assert_eq!(DumpFormat::try_from("textproto").unwrap(), DumpFormat::Textproto);
        assert_eq!(
            DumpFormat::try_from("foobar").unwrap(),
            DumpFormat::Custom("foobar".to_owned())
        );
    }

    #[test]
    fn test_dump_parsed_flags_protobuf_format() {
        let expected = protobuf::text_format::parse_from_str::<ProtoParsedFlags>(
            crate::test::TEST_FLAGS_TEXTPROTO,
        )
        .unwrap()
        .write_to_bytes()
        .unwrap();
        let parsed_flags = parse_test_flags();
        let actual =
            dump_parsed_flags(parsed_flags.parsed_flag.into_iter(), DumpFormat::Protobuf).unwrap();
        assert_eq!(expected, actual);
    }

    #[test]
    fn test_dump_parsed_flags_textproto_format() {
        let parsed_flags = parse_test_flags();
        let bytes =
            dump_parsed_flags(parsed_flags.parsed_flag.into_iter(), DumpFormat::Textproto).unwrap();
        let text = std::str::from_utf8(&bytes).unwrap();
        assert_eq!(crate::test::TEST_FLAGS_TEXTPROTO.trim(), text.trim());
    }

    #[test]
    fn test_dump_parsed_flags_custom_format() {
        macro_rules! assert_dump_parsed_flags_custom_format_contains {
            ($format:expr, $expected:expr) => {
                let parsed_flags = parse_test_flags();
                let bytes = dump_parsed_flags(
                    parsed_flags.parsed_flag.into_iter(),
                    $format.try_into().unwrap(),
                )
                .unwrap();
                let text = std::str::from_utf8(&bytes).unwrap();
                assert!(text.contains($expected));
            };
        }

        // custom format
        assert_dump_parsed_flags_custom_format_contains!(
            "{fully_qualified_name}={permission} + {state}",
            "com.android.aconfig.test.enabled_ro=READ_ONLY + ENABLED"
        );
    }

    #[test]
    fn test_dump_custom_format() {
        macro_rules! assert_custom_format {
            ($format:expr, $expected:expr) => {
                let flag = parse_enabled_ro_flag();
                let mut bytes = vec![];
                dump_custom_format(&flag, $format, &mut bytes);
                let text = std::str::from_utf8(&bytes).unwrap();
                assert_eq!(text, $expected);
            };
        }

        assert_custom_format!("{package}""com.android.aconfig.test\n");
        assert_custom_format!("{name}""enabled_ro\n");
        assert_custom_format!("{namespace}""aconfig_test\n");
        assert_custom_format!("{description}""This flag is ENABLED + READ_ONLY\n");
        assert_custom_format!("{bug}""abc\n");
        assert_custom_format!("{state}""ENABLED\n");
        assert_custom_format!("{state:bool}""true\n");
        assert_custom_format!("{permission}""READ_ONLY\n");
        assert_custom_format!("{trace}""tests/test.aconfig: READ_WRITE + DISABLED, tests/first.values: READ_WRITE + DISABLED, tests/second.values: READ_ONLY + ENABLED\n");
        assert_custom_format!(
            "{trace:paths}",
            "tests/test.aconfig, tests/first.values, tests/second.values\n"
        );
        assert_custom_format!("{is_fixed_read_only}""false\n");
        assert_custom_format!("{is_exported}""false\n");
        assert_custom_format!("{container}""system\n");
        assert_custom_format!("{metadata.purpose}""PURPOSE_BUGFIX\n");
        assert_custom_format!("{metadata.storage}""NONE\n");

        assert_custom_format!("name={name}|state={state}""name=enabled_ro|state=ENABLED\n");
        assert_custom_format!("{state}{state}{state}""ENABLEDENABLEDENABLED\n");
    }

    #[test]
    fn test_create_filter_predicate() {
        macro_rules! assert_create_filter_predicate {
            ($filter:expr, $expected:expr) => {
                let parsed_flags = parse_test_flags();
                let predicate = create_filter_predicate($filter).unwrap();
                let mut filtered_flags: Vec<String> = parsed_flags
                    .parsed_flag
                    .into_iter()
                    .filter(predicate)
                    .map(|flag| flag.fully_qualified_name())
                    .collect();
                filtered_flags.sort();
                assert_eq!(&filtered_flags, $expected);
            };
        }

        assert_create_filter_predicate!(
            "package:com.android.aconfig.test",
            &[
                "com.android.aconfig.test.disabled_ro",
                "com.android.aconfig.test.disabled_rw",
                "com.android.aconfig.test.disabled_rw_exported",
                "com.android.aconfig.test.disabled_rw_in_other_namespace",
                "com.android.aconfig.test.enabled_fixed_ro",
                "com.android.aconfig.test.enabled_fixed_ro_exported",
                "com.android.aconfig.test.enabled_ro",
                "com.android.aconfig.test.enabled_ro_exported",
                "com.android.aconfig.test.enabled_rw",
            ]
        );
        assert_create_filter_predicate!(
            "name:disabled_rw",
            &["com.android.aconfig.test.disabled_rw"]
        );
        assert_create_filter_predicate!(
            "namespace:other_namespace",
            &["com.android.aconfig.test.disabled_rw_in_other_namespace"]
        );
        assert_create_filter_predicate!(
            "namespace:!other_namespace",
            &[
                "com.android.aconfig.test.disabled_ro",
                "com.android.aconfig.test.disabled_rw",
                "com.android.aconfig.test.disabled_rw_exported",
                "com.android.aconfig.test.enabled_fixed_ro",
                "com.android.aconfig.test.enabled_fixed_ro_exported",
                "com.android.aconfig.test.enabled_ro",
                "com.android.aconfig.test.enabled_ro_exported",
                "com.android.aconfig.test.enabled_rw",
            ]
        );
        // description: not supported yet
        assert_create_filter_predicate!("bug:123", &["com.android.aconfig.test.disabled_ro",]);
        assert_create_filter_predicate!(
            "state:ENABLED",
            &[
                "com.android.aconfig.test.enabled_fixed_ro",
                "com.android.aconfig.test.enabled_fixed_ro_exported",
                "com.android.aconfig.test.enabled_ro",
                "com.android.aconfig.test.enabled_ro_exported",
                "com.android.aconfig.test.enabled_rw",
            ]
        );
        assert_create_filter_predicate!(
            "permission:READ_ONLY",
            &[
                "com.android.aconfig.test.disabled_ro",
                "com.android.aconfig.test.enabled_fixed_ro",
                "com.android.aconfig.test.enabled_fixed_ro_exported",
                "com.android.aconfig.test.enabled_ro",
                "com.android.aconfig.test.enabled_ro_exported",
            ]
        );
        // trace: not supported yet
        assert_create_filter_predicate!(
            "is_fixed_read_only:true",
            &[
                "com.android.aconfig.test.enabled_fixed_ro",
                "com.android.aconfig.test.enabled_fixed_ro_exported",
            ]
        );
        assert_create_filter_predicate!(
            "is_exported:true",
            &[
                "com.android.aconfig.test.disabled_rw_exported",
                "com.android.aconfig.test.enabled_fixed_ro_exported",
                "com.android.aconfig.test.enabled_ro_exported",
            ]
        );
        assert_create_filter_predicate!(
            "container:system",
            &[
                "com.android.aconfig.test.disabled_ro",
                "com.android.aconfig.test.disabled_rw",
                "com.android.aconfig.test.disabled_rw_exported",
                "com.android.aconfig.test.disabled_rw_in_other_namespace",
                "com.android.aconfig.test.enabled_fixed_ro",
                "com.android.aconfig.test.enabled_fixed_ro_exported",
                "com.android.aconfig.test.enabled_ro",
                "com.android.aconfig.test.enabled_ro_exported",
                "com.android.aconfig.test.enabled_rw",
            ]
        );
        assert_create_filter_predicate!(
            "metadata.purpose:PURPOSE_BUGFIX",
            &["com.android.aconfig.test.enabled_ro",]
        );
        assert_create_filter_predicate!(
            "metadata.storage:NONE",
            &[
                "com.android.aconfig.test.disabled_ro",
                "com.android.aconfig.test.enabled_fixed_ro",
                "com.android.aconfig.test.enabled_fixed_ro_exported",
                "com.android.aconfig.test.enabled_ro",
                "com.android.aconfig.test.enabled_ro_exported"
            ]
        );

        // synthesized fields
        assert_create_filter_predicate!(
            "fully_qualified_name:com.android.aconfig.test.disabled_rw",
            &["com.android.aconfig.test.disabled_rw"]
        );

        // multiple sub filters
        assert_create_filter_predicate!(
            "permission:READ_ONLY+state:ENABLED",
            &[
                "com.android.aconfig.test.enabled_fixed_ro",
                "com.android.aconfig.test.enabled_fixed_ro_exported",
                "com.android.aconfig.test.enabled_ro",
                "com.android.aconfig.test.enabled_ro_exported",
            ]
        );
    }
}

Messung V0.5 in Prozent
C=97 H=96 G=96

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet am  2026-06-28) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.