diff --git a/docs/source/components/analyse.rst b/docs/source/components/analyse.rst index b57007d..2b47c5a 100644 --- a/docs/source/components/analyse.rst +++ b/docs/source/components/analyse.rst @@ -47,7 +47,7 @@ Limitations **Current Limitations:** -- **Language Support**: C/C++ (``//``, ``/* */``), C# (``//``, ``/* */``, ``///``), Python (``#``) and YAML (``#``) comment styles are supported +- **Language Support**: C/C++ (``//``, ``/* */``), C# (``//``, ``/* */``, ``///``), Python (``#``), YAML (``#``) and Rust (``//``, ``/* */``, ``///``) comment styles are supported - **Single Comment Style**: Each analysis run processes only one comment style at a time Extraction Examples diff --git a/docs/source/components/configuration.rst b/docs/source/components/configuration.rst index d2fd522..99fe8fa 100644 --- a/docs/source/components/configuration.rst +++ b/docs/source/components/configuration.rst @@ -267,7 +267,7 @@ Specifies the comment syntax style used in the source code files. This determine **Type:** ``str`` **Default:** ``"cpp"`` -**Supported values:** ``"cpp"``, ``"python"``, ``"cs"``, ``"yaml"`` +**Supported values:** ``"cpp"``, ``"python"``, ``"cs"``, ``"yaml"``, ``"rust"`` .. code-block:: toml @@ -304,8 +304,15 @@ Specifies the comment syntax style used in the source code files. This determine - ``"yaml"`` - ``#`` (single-line) - ``.yaml``, ``.yml`` + * - Rust + - ``"rust"`` + - ``//`` (single-line), + ``/* */`` (multi-line), + ``///`` (doc comments), + ``//!`` (inner doc comments) + - ``.rs`` -.. note:: Future versions may support additional programming languages. Currently, only C/C++ and Python comment styles are supported. +.. note:: Future versions may support additional programming languages. gitignore ^^^^^^^^^ diff --git a/pyproject.toml b/pyproject.toml index ae7b5f7..51d968c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ dependencies = [ "tree-sitter~=0.25.1", "tree-sitter-c-sharp>=0.23.1", "tree-sitter-yaml>=0.7.1", + "tree-sitter-rust>=0.23.0", ] [build-system] diff --git a/src/sphinx_codelinks/analyse/utils.py b/src/sphinx_codelinks/analyse/utils.py index d9c6dab..1d4efab 100644 --- a/src/sphinx_codelinks/analyse/utils.py +++ b/src/sphinx_codelinks/analyse/utils.py @@ -20,6 +20,15 @@ CommentType.cpp: {"function_definition", "class_definition"}, CommentType.cs: {"method_declaration", "class_declaration", "property_declaration"}, CommentType.yaml: {"block_mapping_pair", "block_sequence_item", "document"}, + # @Rust Scope Node Types, IMPL_RUST_2, impl, [FE_RUST]; + CommentType.rust: { + "function_item", + "struct_item", + "enum_item", + "impl_item", + "trait_item", + "mod_item", + }, } # initialize logger @@ -47,6 +56,10 @@ CPP_QUERY = """(comment) @comment""" C_SHARP_QUERY = """(comment) @comment""" YAML_QUERY = """(comment) @comment""" +RUST_QUERY = """ + (line_comment) @comment + (block_comment) @comment +""" def is_text_file(filepath: Path, sample_size: int = 2048) -> bool: @@ -86,6 +99,11 @@ def init_tree_sitter(comment_type: CommentType) -> tuple[Parser, Query]: parsed_language = Language(tree_sitter_yaml.language()) query = Query(parsed_language, YAML_QUERY) + elif comment_type == CommentType.rust: + import tree_sitter_rust # noqa: PLC0415 + + parsed_language = Language(tree_sitter_rust.language()) + query = Query(parsed_language, RUST_QUERY) else: raise ValueError(f"Unsupported comment style: {comment_type}") parser = Parser(parsed_language) diff --git a/src/sphinx_codelinks/source_discover/config.py b/src/sphinx_codelinks/source_discover/config.py index eb17a42..df0e45c 100644 --- a/src/sphinx_codelinks/source_discover/config.py +++ b/src/sphinx_codelinks/source_discover/config.py @@ -10,6 +10,7 @@ "python": ["py"], "cs": ["cs"], "yaml": ["yml", "yaml"], + "rust": ["rs"], } @@ -18,6 +19,8 @@ class CommentType(str, Enum): cpp = "cpp" cs = "cs" yaml = "yaml" + # @Support Rust style comments, IMPL_RUST_1, impl, [FE_RUST]; + rust = "rust" class SourceDiscoverSectionConfigType(TypedDict, total=False): diff --git a/tests/data/rust/demo.rs b/tests/data/rust/demo.rs new file mode 100644 index 0000000..8c38ab4 --- /dev/null +++ b/tests/data/rust/demo.rs @@ -0,0 +1,31 @@ +// demo.rs + +/// This is a doc comment for the main function +/// @Main function implementation, main_demo, impl, [REQ_001] +fn main() { + println!("Hello from Rust!"); + process_data(); +} + +// @Data processing function, process_func, impl, [REQ_002] +fn process_data() { + let data = vec![1, 2, 3]; + for item in data { + println!("Processing: {}", item); + } +} + +/* Block comment with marker + @User data structure, struct_def, impl, [REQ_003] +*/ +struct User { + name: String, + age: u32, +} + +impl User { + // @User constructor method, new_user, impl, [REQ_004] + fn new(name: String, age: u32) -> Self { + User { name, age } + } +} diff --git a/tests/test_analyse.py b/tests/test_analyse.py index 43ea83b..9f74ca7 100644 --- a/tests/test_analyse.py +++ b/tests/test_analyse.py @@ -104,6 +104,20 @@ def test_analyse(src_dir, src_paths, tmp_path, snapshot_marks): "warnings_path_exists": True, }, ), + ( + TEST_DIR / "data" / "rust", + [ + TEST_DIR / "data" / "rust" / "demo.rs", + ], + ONELINE_COMMENT_STYLE_DEFAULT, + { + "num_src_files": 1, + "num_uncached_files": 1, + "num_cached_files": 0, + "num_comments": 6, + "num_oneline_warnings": 0, + }, + ), ], ) def test_analyse_oneline_needs( diff --git a/tests/test_analyse_utils.py b/tests/test_analyse_utils.py index c9a8b4c..207b1f9 100644 --- a/tests/test_analyse_utils.py +++ b/tests/test_analyse_utils.py @@ -9,6 +9,7 @@ import tree_sitter_c_sharp import tree_sitter_cpp import tree_sitter_python +import tree_sitter_rust import tree_sitter_yaml from sphinx_codelinks.analyse import utils @@ -48,6 +49,14 @@ def init_yaml_tree_sitter() -> tuple[Parser, Query]: return parser, query +@pytest.fixture(scope="session") +def init_rust_tree_sitter() -> tuple[Parser, Query]: + parsed_language = Language(tree_sitter_rust.language()) + query = Query(parsed_language, utils.RUST_QUERY) + parser = Parser(parsed_language) + return parser, query + + @pytest.mark.parametrize( ("code", "result"), [ @@ -284,6 +293,78 @@ def test_find_associated_scope_yaml(code, result, init_yaml_tree_sitter): assert result in yaml_structure +@pytest.mark.parametrize( + ("code", "result"), + [ + ( + b""" + // @req-id: need_001 + fn dummy_func1() { + } + """, + "fn dummy_func1()", + ), + ( + b""" + fn dummy_func2() { + } + // @req-id: need_001 + fn dummy_func1() { + } + """, + "fn dummy_func1()", + ), + ( + b""" + fn dummy_func1() { + let a = 1; + /* @req-id: need_001 */ + } + """, + "fn dummy_func1()", + ), + ( + b""" + fn dummy_func1() { + // @req-id: need_001 + let a = 1; + } + fn dummy_func2() { + } + """, + "fn dummy_func1()", + ), + ( + b""" + /// @req-id: need_001 + fn dummy_func1() { + } + """, + "fn dummy_func1()", + ), + ( + b""" + struct MyStruct { + // @req-id: need_001 + field: i32, + } + """, + "struct MyStruct", + ), + ], +) +def test_find_associated_scope_rust(code, result, init_rust_tree_sitter): + parser, query = init_rust_tree_sitter + comments = utils.extract_comments(code, parser, query) + node: TreeSitterNode | None = utils.find_associated_scope( + comments[0], CommentType.rust + ) + assert node + assert node.text + rust_def = node.text.decode("utf-8") + assert result in rust_def + + @pytest.mark.parametrize( ("code", "result"), [ diff --git a/tests/test_source_discover.py b/tests/test_source_discover.py index 1fdfe8b..3d55917 100644 --- a/tests/test_source_discover.py +++ b/tests/test_source_discover.py @@ -45,7 +45,7 @@ "comment_type": "java", }, [ - "Schema validation error in field 'comment_type': 'java' is not one of ['cpp', 'cs', 'python', 'yaml']" + "Schema validation error in field 'comment_type': 'java' is not one of ['cpp', 'cs', 'python', 'rust', 'yaml']" ], ), ( diff --git a/tests/test_src_trace.py b/tests/test_src_trace.py index 91067c5..8e87a71 100644 --- a/tests/test_src_trace.py +++ b/tests/test_src_trace.py @@ -58,7 +58,7 @@ [ "Project 'dcdc' has the following errors:", "Schema validation error in field 'exclude': 123 is not of type 'string'", - "Schema validation error in field 'comment_type': 'java' is not one of ['cpp', 'cs', 'python', 'yaml']", + "Schema validation error in field 'comment_type': 'java' is not one of ['cpp', 'cs', 'python', 'rust', 'yaml']", "Schema validation error in field 'gitignore': '_true' is not of type 'boolean'", "Schema validation error in field 'include': 345 is not of type 'string'", "Schema validation error in field 'src_dir': ['../dcdc'] is not of type 'string'",