lib-ruby-parser 3.0.5

Ruby parser
Documentation
use super::comment::Comment;
use lib_ruby_parser_nodes::{Field, FieldType, Node};

pub(crate) struct NodeFile<'a> {
    node: &'a Node,
}

impl<'a> NodeFile<'a> {
    pub(crate) fn new(node: &'a Node) -> Self {
        Self { node }
    }

    pub(crate) fn write(&self) {
        std::fs::write(
            &format!("src/nodes/types/{}.rs", self.node.filename),
            self.contents(),
        )
        .unwrap();
    }

    fn contents(&self) -> String {
        format!(
            "{imports}

{comment}
#[derive(Debug, Clone, PartialEq)]
#[repr(C)]
pub struct {struct_name} {{
{fields_declaration}
}}

impl InnerNode for {struct_name} {{
    fn expression(&self) -> &Loc {{
        &self.expression_l
    }}

    fn inspected_children(&self, indent: usize) -> Vec<String> {{
        let mut result = InspectVec::new(indent);
        {inspected_children}
        result.strings()
    }}

    fn str_type(&self) -> &'static str {{
        \"{str_type}\"
    }}

    fn print_with_locs(&self) {{
        println!(\"{{}}\", self.inspect(0));
{print_with_locs}
    }}
}}

",
            imports = self.imports().join("\n"),
            comment = Comment::new(&self.node.comment).to_string(0),
            struct_name = self.node.struct_name,
            fields_declaration = self.fields_declaration().join("\n"),
            inspected_children = self.inspected_children().join("\n        "),
            print_with_locs = self.print_with_locs().join("\n"),
            str_type = self.node.str_type,
        )
    }

    fn fields_declaration(&self) -> Vec<String> {
        self.node
            .fields
            .iter()
            .map(|f| FieldWrapper::new(f).declaration())
            .collect::<Vec<_>>()
    }

    fn inspected_children(&self) -> Vec<String> {
        self.node
            .fields
            .iter()
            .filter_map(|f| FieldWrapper::new(f).print_me_code())
            .collect()
    }

    fn print_with_locs(&self) -> Vec<String> {
        self.node
            .fields
            .iter()
            .filter_map(|f| FieldWrapper::new(f).print_with_locs_me())
            .collect()
    }

    fn imports(&self) -> Vec<&'static str> {
        let mut imports = vec![];
        imports.push("use crate::nodes::InnerNode;");
        imports.push("use crate::nodes::InspectVec;");
        imports.push("use crate::Loc;");
        if self
            .node
            .fields
            .iter()
            .any(|f| f.field_type.has_reference_to_node())
        {
            imports.push("use crate::Node;");
        }
        if self.has_field_with_type(FieldType::StringValue) {
            imports.push("use crate::StringValue;");
        }

        if self.has_field_with_type(FieldType::MaybeNode)
            || self.has_field_with_type(FieldType::RegexOptions)
        {
            imports.push("");
            imports.push("#[cfg(feature = \"compile-with-external-structures\")]");
            imports.push("use crate::containers::ExternalMaybePtr;");
            imports.push("#[cfg(feature = \"compile-with-external-structures\")]");
            imports.push("type MaybePtr<T> = ExternalMaybePtr<T>;");
            imports.push("#[cfg(not(feature = \"compile-with-external-structures\"))]");
            imports.push("type MaybePtr<T> = Option<Box<T>>;");
            imports.push("");
        }

        if self.has_field_with_type(FieldType::Node) {
            imports.push("");
            imports.push("#[cfg(feature = \"compile-with-external-structures\")]");
            imports.push("use crate::containers::ExternalPtr;");
            imports.push("#[cfg(feature = \"compile-with-external-structures\")]");
            imports.push("type Ptr<T> = ExternalPtr<T>;");
            imports.push("#[cfg(not(feature = \"compile-with-external-structures\"))]");
            imports.push("type Ptr<T> = Box<T>;");
            imports.push("");
        }

        if self.has_field_with_type(FieldType::Nodes) {
            imports.push("");
            imports.push("#[cfg(feature = \"compile-with-external-structures\")]");
            imports.push("use crate::containers::ExternalList;");
            imports.push("#[cfg(feature = \"compile-with-external-structures\")]");
            imports.push("type List<T> = ExternalList<T>;");
            imports.push("#[cfg(not(feature = \"compile-with-external-structures\"))]");
            imports.push("type List<T> = Vec<T>;");
            imports.push("");
        }

        if self.has_field_with_type(FieldType::MaybeLoc) {
            imports.push("");
            imports.push("#[cfg(feature = \"compile-with-external-structures\")]");
            imports.push("use crate::containers::ExternalMaybeLoc;");
            imports.push("#[cfg(feature = \"compile-with-external-structures\")]");
            imports.push("type MaybeLoc = ExternalMaybeLoc;");
            imports.push("#[cfg(not(feature = \"compile-with-external-structures\"))]");
            imports.push("type MaybeLoc = Option<Loc>;");
            imports.push("");
        }

        if self.has_field_with_type(FieldType::Str)
            || self.has_field_with_type(FieldType::RawString)
        {
            imports.push("");
            imports.push("#[cfg(feature = \"compile-with-external-structures\")]");
            imports.push("use crate::containers::ExternalStringPtr;");
            imports.push("#[cfg(feature = \"compile-with-external-structures\")]");
            imports.push("type StringPtr = ExternalStringPtr;");
            imports.push("#[cfg(not(feature = \"compile-with-external-structures\"))]");
            imports.push("type StringPtr = String;");
            imports.push("");
        }

        if self.has_field_with_type(FieldType::MaybeStr)
            || self.has_field_with_type(FieldType::Chars)
        {
            imports.push("");
            imports.push("#[cfg(feature = \"compile-with-external-structures\")]");
            imports.push("use crate::containers::ExternalMaybeStringPtr;");
            imports.push("#[cfg(feature = \"compile-with-external-structures\")]");
            imports.push("type MaybeStringPtr = ExternalMaybeStringPtr;");
            imports.push("#[cfg(not(feature = \"compile-with-external-structures\"))]");
            imports.push("type MaybeStringPtr = Option<String>;");
            imports.push("");
        }

        imports
    }

    fn has_field_with_type(&self, field_type: FieldType) -> bool {
        self.node.fields.iter().any(|f| f.field_type == field_type)
    }
}

struct FieldWrapper<'a> {
    field: &'a Field,
}

impl<'a> FieldWrapper<'a> {
    pub(crate) fn new(field: &'a Field) -> Self {
        Self { field }
    }

    pub(crate) fn declaration(&self) -> String {
        format!(
            "{comment}
    pub {field_name}: {field_type},",
            comment = Comment::new(&self.field.comment).to_string(4),
            field_name = self.field.field_name,
            field_type = self.str_field_type()
        )
    }

    pub(crate) fn str_field_type(&self) -> &'static str {
        match self.field.field_type {
            FieldType::Node => "Ptr<Node>",
            FieldType::Nodes => "List<Node>",
            FieldType::MaybeNode => "MaybePtr<Node>",
            FieldType::Loc => "Loc",
            FieldType::MaybeLoc => "MaybeLoc",
            FieldType::Str => "StringPtr",
            FieldType::MaybeStr => "MaybeStringPtr",
            FieldType::Chars => "MaybeStringPtr",
            FieldType::StringValue => "StringValue",
            FieldType::U8 => "u8",
            FieldType::Usize => "usize",
            FieldType::RawString => "StringPtr",
            FieldType::RegexOptions => "MaybePtr<Node>",
        }
    }

    fn print_me_code(&self) -> Option<String> {
        let method_name = match &self.field.field_type {
            FieldType::Node => "push_node",
            FieldType::Nodes => "push_nodes",
            FieldType::MaybeNode => {
                if self.field.always_print {
                    "push_maybe_node_or_nil"
                } else {
                    "push_maybe_node"
                }
            }
            FieldType::Loc => return None,
            FieldType::MaybeLoc => return None,
            FieldType::Str => "push_str",
            FieldType::MaybeStr => "push_maybe_str",
            FieldType::Chars => "push_chars",
            FieldType::StringValue => "push_string_value",
            FieldType::U8 => "push_u8",
            FieldType::Usize => "push_usize",
            FieldType::RawString => "push_raw_str",
            FieldType::RegexOptions => "push_regex_options",
        };

        Some(format!(
            "result.{}(&self.{});",
            method_name, self.field.field_name
        ))
    }

    fn print_with_locs_me(&self) -> Option<String> {
        let offset = "        ";

        match &self.field.field_type {
            FieldType::Node => Some(format!(
                "{offset}self.{field_name}.inner_ref().print_with_locs();",
                offset = offset,
                field_name = self.field.field_name
            )),
            FieldType::Nodes => Some(format!(
                "{offset}for node in self.{field_name}.iter() {{
{offset}    node.inner_ref().print_with_locs();
{offset}}}",
                offset = offset,
                field_name = self.field.field_name
            )),
            FieldType::MaybeNode | FieldType::RegexOptions => Some(format!(
                "{offset}self.{field_name}.as_ref().map(|node| node.inner_ref().print_with_locs());",
                offset = offset,
                field_name = self.field.field_name
            )),
            FieldType::Loc => Some(format!(
                "{offset}self.{field_name}.print(\"{printable_field_name}\");",
                offset = offset,
                field_name = self.field.field_name,
                printable_field_name = self
                    .field
                    .field_name
                    .strip_suffix("_l")
                    .expect("expected loc field to end with _l")
            )),
            FieldType::MaybeLoc => Some(format!(
                "{offset}self.{field_name}.as_ref().map(|loc| loc.print(\"{printable_field_name}\"));",
                offset = offset,
                field_name = self.field.field_name,
                printable_field_name = self
                    .field
                    .field_name
                    .strip_suffix("_l")
                    .expect("expected loc field to end with _l"),
            )),
            FieldType::Str => None,
            FieldType::MaybeStr => None,
            FieldType::Chars => None,
            FieldType::StringValue => None,
            FieldType::U8 => None,
            FieldType::Usize => None,
            FieldType::RawString => None,
        }
    }
}