Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/uniffi_pipeline/src/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 9 kB image not shown  

Quelle  pipeline.rs

  Sprache: Rust
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


use std::{any::type_name, io::Write, marker::PhantomData, process::Command};

use anyhow::{anyhow, Context, Result};
use heck::{ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};

use super::Node;

/// Bindgen pipeline
///
/// Input and Output are root nodes of the input/output IR.  This pipeline converts from `Input` to
/// `Output` using a series of passes.
///
/// See https://mozilla.github.io/uniffi-rs/latest/internals/bindings_ir_pipeline.html for details on
/// how this works.
pub struct Pipeline<Input, Output> {
    passes: Vec<Pass>,
    input: PhantomData<Input>,
    output: PhantomData<Output>,
}

/// A pipeline pass is a function that converts the root node of an IR into another root node
pub struct Pass {
    name: String,
    func: PassFn,
}

type PassFn = Box<dyn FnMut(Box<dyn Node>) -> Result<Box<dyn Node>>>;

pub fn new_pipeline<Input: Node>() -> Pipeline<Input, Input> {
    Pipeline {
        passes: vec![],
        input: PhantomData,
        output: PhantomData,
    }
}

impl<Input: Node, Output: Node> Pipeline<Input, Output> {
    /// Add a pass that converts the current root node into NewOutput
    ///
    /// Under the hood, this uses [Node::into_value] and [Node::try_from_value].
    pub fn convert_ir_pass<NewOutput: Node>(mut self) -> Pipeline<Input, NewOutput> {
        self.passes.push(Pass {
            name: format!("Convert root to {}", type_name::<NewOutput>()),
            func: Box::new(|mut root| {
                let root = NewOutput::try_from_value(root.take_into_value())
                    .map_err(|e| e.into_anyhow())?;
                Ok(Box::new(root))
            }),
        });
        Pipeline {
            passes: self.passes,
            input: self.input,
            output: PhantomData,
        }
    }

    /// Add a pass that mutates nodes in the current IR
    ///
    /// This uses [Node::visit_mut] to find all nodes of a given type, then passes those nodes to
    /// the provided closure to mutate them.
    pub fn pass<F, N>(mut selfmut pass_func: F) -> Self
    where
        F: FnMut(&mut N) -> Result<()> + 'static,
        N: Node,
    {
        self.passes.push(Pass {
            name: type_name::<F>().to_string(),
            func: Box::new(move |mut root| {
                root.try_visit_descendents_recurse_mut(&mut pass_func)?;
                Ok(root)
            }),
        });
        self
    }

    /// Execute the pipeline
    pub fn execute(&mut self, root: Input) -> Result<Output> {
        self.execute_all_passes(root, &mut NullPipelineRecorder)
    }

    /// Execute the pipeline, printing out debugging information for each pass
    ///
    /// This is used to implement the `pipeline` CLI subcommand
    pub fn print_passes(&mut self, root: Input, opts: PrintOptions) -> Result<()> {
        let mut last_output: Option<(tempfile::TempPath, String)> = None;
        let mut recorder = PipelineCliRecorder::new(opts.clone());
        let execute_result = self.execute_all_passes(root, &mut recorder);

        let count = recorder.passes.len();

        for (i, (title, content)) in recorder.passes.into_iter().enumerate() {
            // Save output for diffing
            let mut output = tempfile::NamedTempFile::new()?;
            write!(output, "{content}")?;
            let output_path = output.into_temp_path();

            if opts.matches_pass(&title, i + 1 == count) {
                match (last_output, opts.no_diff) {
                    (None, _) | (Some(_), true) => {
                        // First pass, print out the content
                        let title = format!(" {title} ");
                        println!("{title:=^78}");
                        println!("{content}");
                    }
                    (Some((last_output, last_title)), _) => {
                        // Middle pass, print out the diff from the last run
                        Command::new("diff")
                            .args(["-du""--color=auto"])
                            .arg(&last_output)
                            .arg(&output_path)
                            .arg("--label")
                            .arg(&last_title)
                            .arg("--label")
                            .arg(&title)
                            .spawn()?
                            .wait()?;
                    }
                }
                println!();
            }
            last_output = Some((output_path, title));
        }
        // Check the result after printing all passes.  This gives the user more context when things
        // go wrong.
        execute_result?;
        if matches!(opts.pass.as_deref(), None | Some("final")) {
            if let Some((output_path, _)) = last_output {
                println!("{:=^78}"" final ");
                println!("{}", std::fs::read_to_string(output_path)?);
            }
        }
        Ok(())
    }

    /// Execute each pass in the pipeline and convert `Self::Input` to `Self::Output`
    ///
    /// After each pass, call `recorder.report_pass`, passing it the name of the pass and the root node
    /// after the pass.
    fn execute_all_passes(
        &mut self,
        root: Input,
        recorder: &mut dyn PipelineRecorder,
    ) -> Result<Output> {
        recorder.record_pass("initial", &root);
        let mut root: Box<dyn Node> = Box::new(root);
        for pass in self.passes.iter_mut() {
            root = (pass.func)(root).with_context(|| format!("pass: {}", pass.name))?;
            recorder.record_pass(&pass.name, root.as_ref());
        }
        let root = root
            .to_box_any()
            .downcast::<Output>()
            .map_err(|_| anyhow!("Output type mismatch"))?;
        Ok(*root)
    }
}

/// Records passes taken in a IR pipeline
pub trait PipelineRecorder {
    /// Record the result of a pass for the pipeline CLI
    fn record_pass(&mut self, name: &str, node: &dyn Node);
}

/// Implements PipelineRecorder by doing nothing.  This is what's used when we want to just
/// generate bindings, not print out the passes for the pipeline CLI
struct NullPipelineRecorder;

impl PipelineRecorder for NullPipelineRecorder {
    fn record_pass(&mut self, _name: &str, _node: &dyn Node) {}
}

/// Implements PipelineRecorder for the pipeline CLI
struct PipelineCliRecorder {
    opts: PrintOptions,
    passes: Vec<(String, String)>,
}

impl PipelineCliRecorder {
    fn new(opts: PrintOptions) -> Self {
        Self {
            opts,
            passes: vec![],
        }
    }
}

impl PipelineRecorder for PipelineCliRecorder {
    fn record_pass(&mut self, name: &str, node: &dyn Node) {
        self.passes
            .push((name.to_string(), pass_content(node, &self.opts)));
    }
}

#[derive(Clone)]
pub struct PrintOptions {
    pub pass: Option<String>,
    pub no_diff: bool,
    pub filter_type: Option<String>,
    pub filter_name: Option<String>,
}

impl PrintOptions {
    fn matches_pass(&self, title: &str, last: bool) -> bool {
        match self.pass.as_deref() {
            None => true,
            Some("last") => last,
            Some(p) => title.contains(p),
        }
    }

    fn has_filter(&self) -> bool {
        self.filter_type.is_some() || self.filter_name.is_some()
    }

    fn matches_node(&self, node: &dyn Node, child: &dyn Node) -> bool {
        if let Some(filter_type) = &self.filter_type {
            if node.type_name() != Some(filter_type) {
                return false;
            }
        }
        if let Some(filter_name) = &self.filter_name {
            let Some(string_value) = child.as_any().downcast_ref::<String>() else {
                return false;
            };
            if !(string_value.contains(filter_name)
                || string_value.to_snake_case().contains(filter_name)
                || string_value.to_shouty_snake_case().contains(filter_name)
                || string_value.to_lower_camel_case().contains(filter_name)
                || string_value.to_upper_camel_case().contains(filter_name))
            {
                return false;
            }
        }
        true
    }
}

fn pass_content(node: &dyn Node, opts: &PrintOptions) -> String {
    if !opts.has_filter() {
        return format!("{node:#?}");
    }
    let mut search = NodeFilterSearch::new(opts);
    search.search(node);
    if search.results.is_empty() {
        return "Empty".to_string();
    }
    let mut content = String::new();
    for (path, node_content) in search.results {
        let path = format!(" {path} ");
        content.push_str(&format!("{path:-^78}\n{node_content}\n"));
    }
    content
}

// Implements the depth-first-search to handle `pass_content` with a filter
struct NodeFilterSearch<'a> {
    opts: &'a PrintOptions,
    current_path: Vec<String>,
    results: Vec<(String, String)>,
}

impl<'a> NodeFilterSearch<'a> {
    fn new(opts: &'a PrintOptions) -> Self {
        Self {
            opts,
            current_path: vec!["root".to_string()],
            results: vec![],
        }
    }

    fn search(&mut self, node: &dyn Node) {
        // If any child nodes match, then add this node to the results
        let mut child_match = false;
        node.visit_children(&mut |_, child| {
            child_match = child_match || self.opts.matches_node(node, child);
            Ok(())
        })
        .unwrap();
        if child_match {
            self.results
                .push((self.current_path.join(""), format!("{node:#?}")));
        } else {
            // Otherwise, continue recursing
            node.visit_children(&mut |field_name, child| {
                self.current_path.push(field_name.to_string());
                self.search(child);
                self.current_path.pop();
                Ok(())
            })
            .unwrap();
        }
    }
}

Messung V0.5 in Prozent
C=85 H=95 G=90

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© 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.