taplo 0.1.0

A TOML parser, analyzer and formatter library
Documentation
use super::RewriteNode;
use crate::{
    dom::{self, rewrite},
    syntax::{SyntaxElement, SyntaxNode},
};
use rowan::{GreenNodeBuilder, NodeOrToken, TextRange, TextSize};
use std::collections::{HashMap, HashSet};

#[derive(Debug, Default)]
struct RewriteInfo {
    removes: HashSet<TextRange>,
    inserts: HashMap<TextSize, rewrite::Node>,
}

fn rewrite_tree(elem: SyntaxElement, info: &mut RewriteInfo, builder: &mut GreenNodeBuilder) {
    match info.removes.take(&elem.text_range()) {
        Some(to_remove) => {
            if let Some(to_insert) = info.inserts.remove(&to_remove.start()) {
                to_insert.into_tree(builder);
            }
        }
        None => match elem {
            NodeOrToken::Node(node) => {
                builder.start_node(node.kind().into());

                for child in node.children_with_tokens() {
                    rewrite_tree(child, info, builder);
                }

                builder.finish_node();
            }
            NodeOrToken::Token(token) => {
                builder.token(token.kind().into(), token.text().clone());
            }
        },
    }
}

impl dom::RootNode {
    pub fn rewrite<F: Fn(dom::Path, dom::Node) -> RewriteNode + 'static>(
        self,
        rewrite_fn: F,
    ) -> String {
        let mut info = RewriteInfo::default();
        let root_syntax = self.syntax.clone();
        self.rewrite_impl(dom::Path::new(), &rewrite_fn, &mut info);
        let mut builder = GreenNodeBuilder::new();
        rewrite_tree(root_syntax, &mut info, &mut builder);
        SyntaxNode::new_root(builder.finish()).to_string()
    }

    fn rewrite_impl(
        self,
        path: dom::Path,
        rewrite_fn: &dyn Fn(dom::Path, dom::Node) -> RewriteNode,
        info: &mut RewriteInfo,
    ) {
        let text_range = self.syntax.text_range();

        match rewrite_fn(dom::Path::new(), self.into()) {
            RewriteNode::Old(n) => match n {
                dom::Node::Root(r) => {
                    for (k, entry) in r.entries {
                        dom::Node::from(entry).rewrite_impl(
                            path.join(k.full_key_string_stripped()),
                            rewrite_fn,
                            info,
                        )
                    }
                }
                _ => unreachable!(),
            },
            RewriteNode::New(n) => {
                info.removes.insert(text_range);
                info.inserts.insert(text_range.start(), n);
            }
        }
    }
}

impl dom::TableNode {
    fn rewrite_impl(
        self,
        path: dom::Path,
        rewrite_fn: &dyn Fn(dom::Path, dom::Node) -> RewriteNode,
        info: &mut RewriteInfo,
    ) {
        match rewrite_fn(path.clone(), self.into()) {
            RewriteNode::Old(n) => match n {
                dom::Node::Table(t) => {
                    if let Some(k) = t.key() {
                        k.clone().rewrite_impl(path.clone(), rewrite_fn, info);
                    }
                    for (key, entry) in t.entries {
                        dom::Node::from(entry).rewrite_impl(
                            path.join(key.full_key_string_stripped()),
                            rewrite_fn,
                            info,
                        )
                    }
                }
                _ => {
                    panic!("the returned old node must be the one received in the rewrite function")
                }
            },
            RewriteNode::New(_) => {}
        }
    }
}

impl dom::EntryNode {
    fn rewrite_impl(
        self,
        path: dom::Path,
        rewrite_fn: &dyn Fn(dom::Path, dom::Node) -> RewriteNode,
        info: &mut RewriteInfo,
    ) {
        let ranges = self.text_ranges();
        let start_range = ranges.first().cloned().unwrap();
        match rewrite_fn(path.clone(), self.into()) {
            RewriteNode::Old(old) => {
                match old {
                    dom::Node::Entry(entry) => {
                        entry
                            .key
                            .rewrite_impl(path.clone(), rewrite_fn, info);
                        entry
                            .value
                            .rewrite_impl(path, rewrite_fn, info);
                    }
                    _ => panic!(
                        "the returned old node must be the one received in the rewrite function"
                    ),
                };
            }
            RewriteNode::New(new) => {
                info.removes.extend(ranges);
                info.inserts.insert(start_range.start(), new);
            }
        }
    }
}

impl dom::KeyNode {
    fn rewrite_impl(
        self,
        path: dom::Path,
        rewrite_fn: &dyn Fn(dom::Path, dom::Node) -> RewriteNode,
        info: &mut RewriteInfo,
    ) {
        let ranges = self.text_ranges();
        match rewrite_fn(path, self.into()) {
            RewriteNode::Old(old) => {
                match old {
                    dom::Node::Key(_) => {}
                    _ => panic!(
                        "the returned old node must be the one received in the rewrite function"
                    ),
                };
            }
            RewriteNode::New(new) => {
                info.removes.extend(ranges.clone());
                for range in ranges {
                    info.inserts.insert(range.start(), new.clone());
                }
            }
        }
    }
}

impl dom::ValueNode {
    fn rewrite_impl(
        self,
        path: dom::Path,
        rewrite_fn: &dyn Fn(dom::Path, dom::Node) -> RewriteNode,
        info: &mut RewriteInfo,
    ) {
        let ranges = self.text_ranges();
        let start_range = ranges.first().cloned().unwrap();
        match rewrite_fn(path.clone(), self.into()) {
            RewriteNode::Old(old) => {
                match old {
                    dom::Node::Value(v) => match v {
                        dom::ValueNode::Array(arr) => arr.rewrite_impl(path, rewrite_fn, info),
                        dom::ValueNode::Table(t) => t.rewrite_impl(path, rewrite_fn, info),
                        dom::ValueNode::Empty => unreachable!(),
                        _ => {}
                    },
                    _ => panic!(
                        "the returned old node must be the one received in the rewrite function"
                    ),
                };
            }
            RewriteNode::New(new) => {
                info.removes.extend(ranges);
                info.inserts.insert(start_range.start(), new);
            }
        }
    }
}

impl dom::ArrayNode {
    fn rewrite_impl(
        self,
        path: dom::Path,
        rewrite_fn: &dyn Fn(dom::Path, dom::Node) -> RewriteNode,
        info: &mut RewriteInfo,
    ) {
        let ranges = self.text_ranges();
        let start_range = ranges.first().cloned().unwrap();
        match rewrite_fn(path.clone(), self.into()) {
            RewriteNode::Old(old) => {
                match old {
                    dom::Node::Array(v) => {
                        for (i, item) in v.items.into_iter().enumerate() {
                            item.rewrite_impl(path.join(i), rewrite_fn, info);
                        }
                    }
                    _ => panic!(
                        "the returned old node must be the one received in the rewrite function"
                    ),
                };
            }
            RewriteNode::New(new) => {
                info.removes.extend(ranges);
                info.inserts.insert(start_range.start(), new);
            }
        }
    }
}

impl dom::Node {
    fn rewrite_impl(
        self,
        path: dom::Path,
        rewrite_fn: &dyn Fn(dom::Path, dom::Node) -> RewriteNode,
        info: &mut RewriteInfo,
    ) {
        match self {
            dom::Node::Root(v) => v.rewrite_impl(path, rewrite_fn, info),
            dom::Node::Table(v) => v.rewrite_impl(path, rewrite_fn, info),
            dom::Node::Entry(v) => v.rewrite_impl(path, rewrite_fn, info),
            dom::Node::Key(v) => v.rewrite_impl(path, rewrite_fn, info),
            dom::Node::Value(v) => v.rewrite_impl(path, rewrite_fn, info),
            dom::Node::Array(v) => v.rewrite_impl(path, rewrite_fn, info),
        }
    }
}