use crate::codegen::rust::nodes::helpers::filename;
use lib_ruby_parser_nodes::{template::*, Node, NodeField};
const TEMPLATE: &str = "// This file is auto-generated by {{ helper generated-by }}
{{ helper imports }}
use super::internal::Internal{{ helper node-camelcase-name }};
use crate::blobs::{HasBlob, Blob};
{{ helper node-comment }}
#[repr(C)]
pub struct {{ helper node-camelcase-name }} {
pub(crate) blob: Blob<Self>
}
impl std::fmt::Debug for {{ helper node-camelcase-name }} {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(\"{{ helper node-camelcase-name }}\")
{{ each node-field }}<dnl>
.field(\"{{ helper node-field-rust-field-name }}\", &self.get_{{ helper node-field-name }}())
{{ end }}<dnl>
.finish()
}
}
impl PartialEq for {{ helper node-camelcase-name }} {
fn eq(&self, other: &Self) -> bool {
{{ each node-field }}<dnl>
if self.get_{{ helper node-field-name }}() != other.get_{{ helper node-field-name }}() {
return false
}
{{ end }}<dnl>
true
}
}
impl {{ helper node-camelcase-name }} {
// getters
{{ each node-field }}<dnl>
/// Returns `{{ helper node-field-rust-field-name }}` field
pub fn get_{{ helper node-field-name }}(&self) -> &{{ helper node-field-rust-field-type }} {
unsafe {
#[allow(trivial_casts)]
({{ helper external-getter-name }}(&self.blob) as *const {{ helper node-field-rust-field-type }})
.as_ref()
.unwrap()
}
}
{{ end }}<dnl>
// setters
{{ each node-field }}<dnl>
/// Sets `{{ helper node-field-rust-field-name }}` field
pub fn set_{{ helper node-field-name }}(&mut self, {{ helper node-field-rust-field-name }}: {{ helper node-field-rust-field-type }}) {
unsafe { {{ helper external-setter-name }}(&mut self.blob, {{ helper node-field-rust-field-name }}.into_blob()) }
}
{{ end }}<dnl>
#[allow(dead_code)]
pub(crate) fn into_internal(self) -> Internal{{ helper node-camelcase-name }} {
unsafe { {{ helper external-into-internal-name }}(self.into_blob()) }
}
}
extern \"C\" {
{{ each node-field }}<dnl>
fn {{ helper external-getter-name }}(blob: *const Blob<{{ helper node-camelcase-name }}>) -> *mut Blob<{{ helper node-field-rust-field-type }}>;
fn {{ helper external-setter-name }}(blob: *mut Blob<{{ helper node-camelcase-name }}>, blob: Blob<{{ helper node-field-rust-field-type }}>);
{{ end }}<dnl>
fn {{ helper external-into-internal-name }}(blob: Blob<{{ helper node-camelcase-name }}>) -> Internal{{ helper node-camelcase-name }};
fn {{ helper external-drop-name }}(blob: *mut Blob<{{ helper node-camelcase-name }}>);
}
impl InnerNode for {{ helper node-camelcase-name }} {
fn expression(&self) -> &Loc {
self.get_expression_l()
}
fn inspected_children(&self, indent: usize) -> Vec<String> {
let mut result = InspectVec::new(indent);
{{ each node-field }}<dnl>
{{ helper inspect-field }}
{{ end }}<dnl>
result.strings()
}
fn str_type(&self) -> &'static str {
\"{{ helper node-str-type }}\"
}
fn print_with_locs(&self) {
println!(\"{}\", self.inspect(0));
{{ each node-field }}<dnl>
{{ helper print-field-with-loc }}
{{ end }}<dnl>
}
}
impl Drop for {{ helper node-camelcase-name }} {
fn drop(&mut self) {
unsafe { {{ helper external-drop-name }}(&mut self.blob) }
}
}";
pub(crate) fn codegen(node: &lib_ruby_parser_nodes::Node) {
let template = NodeTemplateRoot::new(TEMPLATE).unwrap();
let mut fns = crate::codegen::fns::default_fns!();
fns.register::<NodeField, F::Helper>(
"external-getter-name",
lib_ruby_parser_bindings::helpers::nodes::field_getter::name,
);
fns.register::<NodeField, F::Helper>(
"external-setter-name",
lib_ruby_parser_bindings::helpers::nodes::field_setter::name,
);
fns.register::<Node, F::Helper>(
"external-into-internal-name",
lib_ruby_parser_bindings::helpers::nodes::into_internal::name,
);
fns.register::<Node, F::Helper>(
"external-drop-name",
lib_ruby_parser_bindings::helpers::nodes::drop_variant::name,
);
fns.register::<Node, F::Helper>("imports", local_helpers::imports);
fns.register::<NodeField, F::Helper>("inspect-field", local_helpers::inspect_field);
fns.register::<NodeField, F::Helper>(
"print-field-with-loc",
local_helpers::print_field_with_loc,
);
let contents = template.render(node, &fns);
let dir = filename(node);
let path = format!("src/nodes/types/{}/external.rs", dir);
std::fs::write(&path, contents).unwrap();
}
mod local_helpers {
use lib_ruby_parser_nodes::{node_has_field, Node};
use super::*;
pub(crate) fn imports(node: &Node) -> String {
let mut imports = vec![];
imports.push("use crate::nodes::InnerNode;");
imports.push("use crate::nodes::InspectVec;");
imports.push("use crate::Loc;");
use lib_ruby_parser_nodes::NodeFieldType::*;
if node_has_field!(node, Node | Nodes | MaybeNode { .. }) {
imports.push("use crate::Node;");
}
if node_has_field!(node, StringValue) {
imports.push("use crate::Bytes;");
}
if node_has_field!(node, MaybeNode { .. } | MaybeLoc | MaybeStr { .. }) {
imports.push("use crate::containers::ExternalMaybe as Maybe;");
}
if node_has_field!(node, Node | MaybeNode { .. }) {
imports.push("use crate::containers::ExternalPtr as Ptr;");
}
if node_has_field!(node, Nodes) {
imports.push("use crate::containers::ExternalList as List;");
}
if node_has_field!(node, Str { .. } | MaybeStr { .. }) {
imports.push("use crate::containers::ExternalStringPtr as StringPtr;");
}
imports.join("\n")
}
pub(crate) fn inspect_field(node_field: &NodeField) -> String {
use lib_ruby_parser_nodes::NodeFieldType::*;
let method_name = match node_field.field_type {
Node => "push_node",
Nodes => "push_nodes",
MaybeNode { regexp_options } => {
if regexp_options {
"push_regex_options"
} else if node_field.always_print {
"push_maybe_node_or_nil"
} else {
"push_maybe_node"
}
}
Loc => return format!(""),
MaybeLoc => return format!(""),
Str { raw } => {
if raw {
"push_raw_str"
} else {
"push_str"
}
}
MaybeStr { chars } => {
if chars {
"push_chars"
} else {
"push_maybe_str"
}
}
StringValue => "push_string_value",
U8 => "push_u8",
};
format!(
"result.{}(self.get_{}());",
method_name, node_field.snakecase_name
)
}
pub(crate) fn print_field_with_loc(node_field: &NodeField) -> String {
use lib_ruby_parser_nodes::NodeFieldType::*;
match node_field.field_type {
Node => format!(
"self.get_{field_name}().inner_ref().print_with_locs();",
field_name = node_field.snakecase_name
),
Nodes =>
format!(
"for node in self.get_{field_name}().iter() {{ node.inner_ref().print_with_locs(); }}",
field_name = node_field.snakecase_name
),
MaybeNode { .. } => format!(
"if let Some(node) = self.get_{field_name}().as_ref() {{ node.inner_ref().print_with_locs() }}",
field_name = node_field.snakecase_name
),
Loc => format!(
"self.get_{field_name}().print(\"{printable_field_name}\");",
field_name = node_field.snakecase_name,
printable_field_name = node_field
.snakecase_name
.strip_suffix("_l")
.expect("expected loc field to end with _l")
),
MaybeLoc => format!(
"if let Some(loc) = self.get_{field_name}().as_ref() {{ loc.print(\"{printable_field_name}\") }}",
field_name = node_field.snakecase_name,
printable_field_name = node_field
.snakecase_name
.strip_suffix("_l")
.expect("expected loc field to end with _l"),
),
Str { .. } => format!(""),
MaybeStr { .. } => format!(""),
StringValue => format!(""),
U8 => format!(""),
}
}
}