use std::collections::{BTreeSet, HashSet, VecDeque};
use std::rc::{Rc, Weak};
use crate::expression_tree::{BindingExpression, Expression};
use crate::langtype::Type;
use crate::namedreference::NamedReference;
use crate::object_tree::{Component, Document, ElementRc};
#[cfg(feature = "cpp")]
mod cpp;
#[cfg(feature = "rust")]
pub mod rust;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum OutputFormat {
#[cfg(feature = "cpp")]
Cpp,
#[cfg(feature = "rust")]
Rust,
Interpreter,
Llr,
}
impl OutputFormat {
pub fn guess_from_extension(path: &std::path::Path) -> Option<Self> {
match path.extension().and_then(|ext| ext.to_str()) {
#[cfg(feature = "cpp")]
Some("cpp") | Some("cxx") | Some("h") | Some("hpp") => Some(Self::Cpp),
#[cfg(feature = "rust")]
Some("rs") => Some(Self::Rust),
_ => None,
}
}
}
impl std::str::FromStr for OutputFormat {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
#[cfg(feature = "cpp")]
"cpp" => Ok(Self::Cpp),
#[cfg(feature = "rust")]
"rust" => Ok(Self::Rust),
"llr" => Ok(Self::Llr),
_ => Err(format!("Unknown outpout format {}", s)),
}
}
}
pub fn generate(
format: OutputFormat,
destination: &mut impl std::io::Write,
doc: &Document,
) -> std::io::Result<()> {
#![allow(unused_variables)]
#![allow(unreachable_code)]
if matches!(doc.root_component.root_element.borrow().base_type, Type::Invalid | Type::Void) {
return Ok(());
}
match format {
#[cfg(feature = "cpp")]
OutputFormat::Cpp => {
let output = cpp::generate(doc);
write!(destination, "{}", output)?;
}
#[cfg(feature = "rust")]
OutputFormat::Rust => {
let output = rust::generate(doc);
write!(destination, "{}", output)?;
}
OutputFormat::Interpreter => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Unsupported output format: The interpreter is not a valid output format yet.",
)); }
OutputFormat::Llr => {
writeln!(
destination,
"{:#?}",
crate::llr::lower_to_item_tree::lower_to_item_tree(&doc.root_component)
)?;
}
}
Ok(())
}
pub trait ItemTreeBuilder {
type SubComponentState: Clone;
fn push_repeated_item(
&mut self,
item: &crate::object_tree::ElementRc,
repeater_count: u32,
parent_index: u32,
component_state: &Self::SubComponentState,
);
fn push_native_item(
&mut self,
item: &ElementRc,
children_offset: u32,
parent_index: u32,
component_state: &Self::SubComponentState,
);
fn enter_component(
&mut self,
item: &ElementRc,
sub_component: &Rc<Component>,
children_offset: u32,
component_state: &Self::SubComponentState,
) -> Self::SubComponentState;
fn enter_component_children(
&mut self,
item: &ElementRc,
repeater_count: u32,
component_state: &Self::SubComponentState,
sub_component_state: &Self::SubComponentState,
);
}
pub fn build_item_tree<T: ItemTreeBuilder>(
root_component: &Rc<Component>,
initial_state: &T::SubComponentState,
builder: &mut T,
) {
if let Some(sub_component) = root_component.root_element.borrow().sub_component() {
assert!(root_component.root_element.borrow().children.is_empty());
let sub_compo_state =
builder.enter_component(&root_component.root_element, sub_component, 1, initial_state);
builder.enter_component_children(
&root_component.root_element,
0,
initial_state,
&sub_compo_state,
);
build_item_tree::<T>(sub_component, &sub_compo_state, builder);
} else {
let mut repeater_count = 0;
visit_item(initial_state, &root_component.root_element, 1, &mut repeater_count, 0, builder);
visit_children(
initial_state,
&root_component.root_element.borrow().children,
root_component,
&root_component.root_element,
0,
0,
1,
1,
&mut repeater_count,
builder,
);
}
fn item_sub_tree_size(e: &ElementRc) -> usize {
let mut count = e.borrow().children.len();
if let Some(sub_component) = e.borrow().sub_component() {
count += item_sub_tree_size(&sub_component.root_element);
}
for i in &e.borrow().children {
count += item_sub_tree_size(i);
}
count
}
fn visit_children<T: ItemTreeBuilder>(
state: &T::SubComponentState,
children: &Vec<ElementRc>,
component: &Rc<Component>,
parent_item: &ElementRc,
parent_index: u32,
relative_parent_index: u32,
children_offset: u32,
relative_children_offset: u32,
repeater_count: &mut u32,
builder: &mut T,
) {
debug_assert_eq!(
relative_parent_index,
parent_item.borrow().item_index.get().map(|x| *x as u32).unwrap_or(parent_index)
);
if children.is_empty() {
if let Some(nested_subcomponent) = parent_item.borrow().sub_component() {
let sub_component_state = builder.enter_component(
parent_item,
nested_subcomponent,
children_offset,
state,
);
visit_children(
&sub_component_state,
&nested_subcomponent.root_element.borrow().children,
nested_subcomponent,
&nested_subcomponent.root_element,
parent_index,
relative_parent_index,
children_offset,
relative_children_offset,
repeater_count,
builder,
);
return;
}
}
let mut offset = children_offset + children.len() as u32;
let mut sub_component_states = VecDeque::new();
for child in children.iter() {
if let Some(sub_component) = child.borrow().sub_component() {
let sub_component_state =
builder.enter_component(child, sub_component, offset, state);
visit_item(
&sub_component_state,
&sub_component.root_element,
offset,
repeater_count,
parent_index,
builder,
);
sub_component_states.push_back(sub_component_state);
} else {
visit_item(state, child, offset, repeater_count, parent_index, builder);
}
offset += item_sub_tree_size(child) as u32;
}
let mut offset = children_offset + children.len() as u32;
let mut relative_offset = relative_children_offset + children.len() as u32;
let mut index = children_offset;
let mut relative_index = relative_children_offset;
for e in children.iter() {
if let Some(sub_component) = e.borrow().sub_component() {
let sub_tree_state = sub_component_states.pop_front().unwrap();
builder.enter_component_children(e, *repeater_count, state, &sub_tree_state);
visit_children(
&sub_tree_state,
&sub_component.root_element.borrow().children,
sub_component,
&sub_component.root_element,
index,
0,
offset,
1,
repeater_count,
builder,
);
} else {
visit_children(
state,
&e.borrow().children,
component,
e,
index,
relative_index,
offset,
relative_offset,
repeater_count,
builder,
);
}
index += 1;
relative_index += 1;
let size = item_sub_tree_size(e) as u32;
offset += size;
relative_offset += size;
}
}
fn visit_item<T: ItemTreeBuilder>(
component_state: &T::SubComponentState,
item: &ElementRc,
children_offset: u32,
repeater_count: &mut u32,
parent_index: u32,
builder: &mut T,
) {
if item.borrow().repeated.is_some() {
builder.push_repeated_item(item, *repeater_count, parent_index, component_state);
*repeater_count += 1;
} else {
let mut item = item.clone();
let mut component_state = component_state.clone();
while let Some((base, state)) = {
let base = item.borrow().sub_component().map(|c| {
(
c.root_element.clone(),
builder.enter_component(&item, c, children_offset, &component_state),
)
});
base
} {
item = base;
component_state = state;
}
builder.push_native_item(&item, children_offset, parent_index, &component_state)
}
}
}
pub fn handle_property_bindings_init(
component: &Rc<Component>,
mut handle_property: impl FnMut(&ElementRc, &str, &BindingExpression),
) {
fn handle_property_inner(
component: &Weak<Component>,
elem: &ElementRc,
prop_name: &str,
binding_expression: &BindingExpression,
handle_property: &mut impl FnMut(&ElementRc, &str, &BindingExpression),
processed: &mut HashSet<NamedReference>,
) {
let nr = NamedReference::new(elem, prop_name);
if processed.contains(&nr) {
return;
}
processed.insert(nr);
if binding_expression.analysis.as_ref().map_or(false, |a| a.is_const) {
binding_expression.expression.visit_recursive(&mut |e| {
if let Expression::PropertyReference(nr) = e {
let elem = nr.element();
if Weak::ptr_eq(&elem.borrow().enclosing_component, component) {
if let Some(be) = elem.borrow().bindings.get(nr.name()) {
handle_property_inner(
component,
&elem,
nr.name(),
&be.borrow(),
handle_property,
processed,
);
}
}
}
})
}
handle_property(elem, prop_name, binding_expression);
}
let mut processed = HashSet::new();
crate::object_tree::recurse_elem(&component.root_element, &(), &mut |elem: &ElementRc, ()| {
for (prop_name, binding_expression) in &elem.borrow().bindings {
handle_property_inner(
&Rc::downgrade(component),
elem,
prop_name,
&binding_expression.borrow(),
&mut handle_property,
&mut processed,
);
}
});
}
pub fn for_each_const_properties(component: &Rc<Component>, mut f: impl FnMut(&ElementRc, &str)) {
crate::object_tree::recurse_elem(&component.root_element, &(), &mut |elem: &ElementRc, ()| {
if elem.borrow().repeated.is_some() {
return;
}
let mut e = elem.clone();
let mut all_prop = BTreeSet::new();
loop {
all_prop.extend(
e.borrow()
.property_declarations
.iter()
.filter(|(_, x)| x.property_type.is_property_type())
.map(|(k, _)| k.clone()),
);
match &e.clone().borrow().base_type {
Type::Component(c) => {
e = c.root_element.clone();
}
Type::Native(n) => {
all_prop.extend(
n.properties
.iter()
.filter(|(k, x)| {
x.ty.is_property_type()
&& !k.starts_with("viewport-")
&& k.as_str() != "commands"
})
.map(|(k, _)| k.clone()),
);
break;
}
_ => break,
}
}
for c in all_prop {
if NamedReference::new(elem, &c).is_constant() {
f(elem, &c);
}
}
});
}