/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* 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};
usesuper::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. pubstruct 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 pubstruct Pass {
name: String,
func: PassFn,
}
type PassFn = Box<dyn FnMut(Box<dyn Node>) -> Result<Box<dyn Node>>>;
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]. pubfn convert_ir_pass<NewOutput: Node>(mutself) -> 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. pubfn pass<F, N>(mutself, mut 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, printing out debugging information for each pass /// /// This is used to implement the `pipeline` CLI subcommand pubfn print_passes(&mutself, root: Input, opts: PrintOptions) -> Result<()> { letmut last_output: Option<(tempfile::TempPath, String)> = None; letmut 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 letmut 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")) { iflet 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(
&mutself,
root: Input,
recorder: &mutdyn PipelineRecorder,
) -> Result<Output> {
recorder.record_pass("initial", &root); letmut root: Box<dyn Node> = Box::new(root); for pass inself.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 pubtrait PipelineRecorder { /// Record the result of a pass for the pipeline CLI fn record_pass(&mutself, 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;
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.