use crate::core::ast::GoAst;
use anyhow::{Context, Result};
use std::path::Path;
use tree_sitter::{Language as TsLanguage, Parser, Tree};
fn get_language() -> TsLanguage {
tree_sitter_go::LANGUAGE.into()
}
pub fn parse_source(content: &str, path: &Path) -> Result<GoAst> {
let mut parser = Parser::new();
let language = get_language();
parser
.set_language(&language)
.context("Failed to set tree-sitter Go language")?;
let tree = parser
.parse(content, None)
.context("Failed to parse Go source")?;
if has_parse_errors(&tree) {
anyhow::bail!("Go parse tree contains syntax errors");
}
Ok(GoAst {
tree,
path: path.to_path_buf(),
source: content.to_string(),
})
}
pub fn has_parse_errors(tree: &Tree) -> bool {
tree.root_node().has_error()
}
pub fn node_text<'a>(node: &tree_sitter::Node, source: &'a str) -> &'a str {
let start = node.start_byte();
let end = node.end_byte();
&source[start..end]
}
pub fn node_line(node: &tree_sitter::Node) -> usize {
node.start_position().row + 1
}
pub fn node_column(node: &tree_sitter::Node) -> usize {
node.start_position().column + 1
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn test_parse_package_with_function() {
let source = "package main\n\nfunc main() {}\n";
let ast = parse_source(source, &PathBuf::from("main.go")).unwrap();
assert!(!has_parse_errors(&ast.tree));
assert_eq!(ast.tree.root_node().kind(), "source_file");
}
#[test]
fn test_parse_methods_imports_structs_and_interfaces() {
let source = r#"package service
import (
"context"
alias "net/http"
)
type Handler struct {
client *alias.Client
}
type Runner interface {
Run(context.Context) error
}
func (h *Handler) Serve(ctx context.Context) error {
return nil
}
"#;
let ast = parse_source(source, &PathBuf::from("handler.go")).unwrap();
assert!(!has_parse_errors(&ast.tree));
}
#[test]
fn test_parse_generated_comment() {
let source = "// Code generated by mockgen. DO NOT EDIT.\npackage mock\n";
let ast = parse_source(source, &PathBuf::from("mock_service.go")).unwrap();
assert!(!has_parse_errors(&ast.tree));
}
#[test]
fn test_malformed_go_fails() {
let source = "package main\n\nfunc broken( {\n";
let result = parse_source(source, &PathBuf::from("broken.go"));
assert!(result.is_err());
}
#[test]
fn test_node_helpers() {
let source = "package main\n\nfunc main() {}\n";
let ast = parse_source(source, &PathBuf::from("main.go")).unwrap();
let root = ast.tree.root_node();
assert_eq!(node_text(&root, &ast.source), source);
assert_eq!(node_line(&root), 1);
assert_eq!(node_column(&root), 1);
}
}