diff --git a/src/cfgnet/plugins/concept/cargo_plugin.py b/src/cfgnet/plugins/concept/cargo_plugin.py new file mode 100644 index 0000000..654a2f7 --- /dev/null +++ b/src/cfgnet/plugins/concept/cargo_plugin.py @@ -0,0 +1,56 @@ +# This file is part of the CfgNet module. +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see . +from cfgnet.plugins.file_type.toml_plugin import TomlPlugin +from cfgnet.config_types.config_types import ConfigType + + +class CargoPlugin(TomlPlugin): + def __init__(self): + super().__init__("cargo") + self.excluded_keys = [ + "description", + "authors", + "maintainers", + "readme", + "keywords", + "classifiers", + ] + + def is_responsible(self, abs_file_path: str) -> bool: + if abs_file_path.endswith("Cargo.toml"): + return True + return False + + @staticmethod + def get_config_type(option_name: str) -> ConfigType: + """ + Find config type based on option name. + + :param option_name: name of option + :return: config type + """ + if option_name in ( + "homepage", + "repository", + "documentation", + "urls", + "url", + ): + return ConfigType.URL + if option_name in ("include", "exclude"): + return ConfigType.PATH + if option_name in ("version", "dependencies", "dev-dependencies"): + return ConfigType.VERSION_NUMBER + return ConfigType.UNKNOWN diff --git a/src/cfgnet/plugins/file_type/toml_plugin.py b/src/cfgnet/plugins/file_type/toml_plugin.py index c9878a7..3df1832 100644 --- a/src/cfgnet/plugins/file_type/toml_plugin.py +++ b/src/cfgnet/plugins/file_type/toml_plugin.py @@ -87,31 +87,10 @@ def _iter_data(self, data, line_number_dict, parent): if isinstance(value, dict): self._iter_data(value, line_number_dict, option) elif isinstance(value, list): - index = 0 - for entry in value: - if isinstance(entry, dict): - virtual_option = OptionNode( - option.name + "_" + str(index), lineno - ) - option.add_child(virtual_option) - self._iter_data( - entry, line_number_dict, virtual_option - ) - index += 1 - else: - name = ( - f"{option.name}:{entry}" - if option.config_type - == ConfigType.VERSION_NUMBER - else entry - ) - option.add_child(ValueNode(name)) + value_node = ValueNode(name=str(value)) + option.add_child(value_node) else: - name = ( - f"{option.name}:{value}" - if option.config_type == ConfigType.VERSION_NUMBER - else value - ) + name = value option.add_child(ValueNode(name)) # pylint: disable=unused-argument diff --git a/src/cfgnet/plugins/plugin_manager.py b/src/cfgnet/plugins/plugin_manager.py index 0896c8a..bef4f9d 100644 --- a/src/cfgnet/plugins/plugin_manager.py +++ b/src/cfgnet/plugins/plugin_manager.py @@ -52,6 +52,7 @@ from cfgnet.plugins.concept.angular_plugin import AngularPlugin from cfgnet.plugins.concept.mapreduce_plugin import MapReducePlugin from cfgnet.plugins.concept.circleci_plugin import CircleCiPlugin +from cfgnet.plugins.concept.cargo_plugin import CargoPlugin class PluginManager: @@ -85,6 +86,7 @@ class PluginManager: AngularPlugin(), MapReducePlugin(), CircleCiPlugin(), + CargoPlugin(), ] file_type_plugins: List[Plugin] = [ diff --git a/tests/cfgnet/plugins/concept/test_cargo_plugin.py b/tests/cfgnet/plugins/concept/test_cargo_plugin.py new file mode 100644 index 0000000..45efb12 --- /dev/null +++ b/tests/cfgnet/plugins/concept/test_cargo_plugin.py @@ -0,0 +1,85 @@ +# This file is part of the CfgNet module. +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see . +import os +import pytest + +from cfgnet.plugins.concept.cargo_plugin import CargoPlugin +from cfgnet.config_types.config_types import ConfigType +from tests.utility.id_creator import make_id + + +@pytest.fixture(name="get_plugin") +def get_plugin_(): + plugin = CargoPlugin() + return plugin + + +def test_is_responsible(get_plugin): + plugin = get_plugin + + assert plugin.is_responsible("tests/files/Cargo.toml") + assert not plugin.is_responsible("tests/files/test.toml") + + +def test_parse_pyproject_file(get_plugin): + plugin = get_plugin + file = os.path.abspath("tests/files/Cargo.toml") + + artifact = plugin.parse_file(file, "Cargo.toml") + nodes = artifact.get_nodes() + ids = {node.id for node in nodes} + + assert artifact is not None + assert len(nodes) == 13 + + assert make_id("Cargo.toml", "file", "Cargo.toml") in ids + assert make_id("Cargo.toml", "package", "name", "example") in ids + assert make_id("Cargo.toml", "package", "version", "0.1.0") in ids + assert make_id("Cargo.toml", "package", "edition", "2021") in ids + assert make_id("Cargo.toml", "package", "license", "MIT") in ids + assert make_id("Cargo.toml", "package", "homepage", "https://example.com") in ids + assert make_id("Cargo.toml", "package", "workspace", "path/to/workspace/root") in ids + assert make_id("Cargo.toml", "dependencies", "serde", "1.0") in ids + assert make_id("Cargo.toml", "dependencies", "rand", "0.8") in ids + assert make_id("Cargo.toml", "dev-dependencies", "tokio", "version", "1") in ids + assert make_id("Cargo.toml", "dev-dependencies", "tokio", "features", "['full']") in ids + assert make_id("Cargo.toml", "features", "default", "['serde']") in ids + assert make_id("Cargo.toml", "profile", "release", "opt-level", "3") in ids + + +def test_config_types(get_plugin): + plugin = get_plugin + file = os.path.abspath("tests/files/Cargo.toml") + + artifact = plugin.parse_file(file, "Cargo.toml") + nodes = artifact.get_nodes() + + name_node = next(filter(lambda x: x.id == make_id("Cargo.toml", "package", "name", "example"), nodes)) + path_node = next(filter(lambda x: x.id == make_id("Cargo.toml", "package", "workspace", "path/to/workspace/root"), nodes)) + number_node = next(filter(lambda x: x.id == make_id("Cargo.toml", "package", "edition", "2021"), nodes)) + dep_node = next(filter(lambda x: x.id == make_id("Cargo.toml", "dependencies", "serde", "1.0"), nodes)) + url_node = next(filter(lambda x: x.id == make_id("Cargo.toml", "package", "homepage", "https://example.com"), nodes)) + version_node = next(filter(lambda x: x.id == make_id("Cargo.toml", "package", "version", "0.1.0"), nodes)) + license_node = next(filter(lambda x: x.id == make_id("Cargo.toml", "package", "license", "MIT"), nodes)) + number_node2 = next(filter(lambda x: x.id == make_id("Cargo.toml", "profile", "release", "opt-level", "3"), nodes)) + + assert path_node.config_type == ConfigType.PATH + assert url_node.config_type == ConfigType.URL + assert license_node.config_type == ConfigType.LICENSE + assert dep_node.config_type == ConfigType.VERSION_NUMBER + assert number_node.config_type == ConfigType.NUMBER + assert version_node.config_type == ConfigType.VERSION_NUMBER + assert name_node.config_type == ConfigType.NAME + assert number_node2.config_type == ConfigType.NUMBER diff --git a/tests/cfgnet/plugins/concept/test_pyproject_plugin.py b/tests/cfgnet/plugins/concept/test_pyproject_plugin.py index 75eddd7..aeaf280 100644 --- a/tests/cfgnet/plugins/concept/test_pyproject_plugin.py +++ b/tests/cfgnet/plugins/concept/test_pyproject_plugin.py @@ -47,88 +47,19 @@ def test_parse_pyproject_file(get_plugin): ids = {node.id for node in nodes} assert artifact is not None - assert len(nodes) == 12 + assert len(nodes) == 11 assert make_id("pyproject.toml", "file", "pyproject.toml") in ids assert make_id("pyproject.toml", "tool", "poetry", "name", "CfgNet") in ids - assert ( - make_id("pyproject.toml", "tool", "poetry", "version", "version:1.0.0") - in ids - ) - assert ( - make_id("pyproject.toml", "tool", "poetry", "include", "test.py") - in ids - ) - assert ( - make_id("pyproject.toml", "tool", "poetry", "exclude", "hello.py") - in ids - ) - assert ( - make_id("pyproject.toml", "tool", "poetry", "license", "GPL-3.0+") - in ids - ) - assert ( - make_id( - "pyproject.toml", - "tool", - "poetry", - "homepage", - "https://github.com", - ) - in ids - ) - assert ( - make_id( - "pyproject.toml", - "tool", - "poetry", - "packages", - "packages_0", - "include", - "cfgnet", - ) - in ids - ) - assert ( - make_id( - "pyproject.toml", - "tool", - "poetry", - "packages", - "packages_0", - "from", - "src", - ) - in ids - ) - assert ( - make_id( - "pyproject.toml", - "tool", - "poetry", - "dependencies", - "python", - "python:^3.8", - ) - in ids - ) - assert ( - make_id( - "pyproject.toml", - "tool", - "poetry", - "dev-dependencies", - "cov", - "cov:5.1", - ) - in ids - ) - assert ( - make_id( - "pyproject.toml", "tool", "poetry", "scripts", "cfgnet", "main" - ) - in ids - ) + assert make_id("pyproject.toml", "tool", "poetry", "version", "1.0.0") in ids + assert make_id("pyproject.toml", "tool", "poetry", "include", "['test.py']") in ids + assert make_id("pyproject.toml", "tool", "poetry", "exclude", "['hello.py']") in ids + assert make_id("pyproject.toml", "tool", "poetry", "license", "GPL-3.0+") in ids + assert make_id("pyproject.toml", "tool", "poetry", "homepage", "https://github.com") in ids + assert make_id("pyproject.toml", "tool", "poetry", "packages", "[{'include': 'cfgnet', 'from': 'src'}]") in ids + assert make_id("pyproject.toml", "tool", "poetry", "dependencies", "python", "^3.8") in ids + assert make_id("pyproject.toml", "tool", "poetry", "dev-dependencies", "cov", "5.1") in ids + assert make_id("pyproject.toml", "tool", "poetry", "scripts", "cfgnet", "main") in ids def test_config_types(get_plugin): diff --git a/tests/cfgnet/plugins/file_type/test_toml_plugin.py b/tests/cfgnet/plugins/file_type/test_toml_plugin.py index a6bd50a..436753b 100644 --- a/tests/cfgnet/plugins/file_type/test_toml_plugin.py +++ b/tests/cfgnet/plugins/file_type/test_toml_plugin.py @@ -46,35 +46,12 @@ def test_parse_toml_file(get_plugin): ids = {node.id for node in nodes} assert artifact is not None - assert len(nodes) == 8 + assert len(nodes) == 7 assert make_id("test.toml", "file", "test.toml") in ids assert make_id("test.toml", "tool", "poetry", "name", "cfgnet") in ids assert make_id("test.toml", "tool", "poetry", "version", "v1.1.0") in ids - assert make_id("test.toml", "tool", "poetry", "keywords", "config") in ids + assert make_id("test.toml", "tool", "poetry", "keywords", "['config']") in ids assert make_id("test.toml", "dependencies", "python", "^3.6") in ids assert make_id("test.toml", "dependencies", "gitpython", "^3.0") in ids - assert ( - make_id( - "test.toml", - "tool", - "poetry", - "packages", - "packages_0", - "include", - "cfgnet", - ) - in ids - ) - assert ( - make_id( - "test.toml", - "tool", - "poetry", - "packages", - "packages_0", - "from", - "src", - ) - in ids - ) + assert make_id("test.toml", "tool", "poetry", "packages", "[{'include': 'cfgnet', 'from': 'src'}]") in ids diff --git a/tests/cfgnet/plugins/test_plugin_manager.py b/tests/cfgnet/plugins/test_plugin_manager.py index d82734c..b4683cd 100644 --- a/tests/cfgnet/plugins/test_plugin_manager.py +++ b/tests/cfgnet/plugins/test_plugin_manager.py @@ -19,42 +19,22 @@ def test_get_all_plugins(): all_plugins = PluginManager.get_plugins() - assert len(all_plugins) == 27 + assert len(all_plugins) == 28 def test_get_responsible_plugin(): plugins = PluginManager.get_plugins() - docker_plugin = PluginManager.get_responsible_plugin( - plugins, "path/to/Dockerfile" - ) - maven_plugin = PluginManager.get_responsible_plugin( - plugins, "path/to/pom.xml" - ) - nodejs_plugin = PluginManager.get_responsible_plugin( - plugins, "path/to/package.json" - ) - docker_compose_plugin = PluginManager.get_responsible_plugin( - plugins, "path/to/docker-compose.yml" - ) - travis_plugin = PluginManager.get_responsible_plugin( - plugins, "path/to/.travis.yml" - ) - cypress_plugin = PluginManager.get_responsible_plugin( - plugins, "path/to/cypress.json" - ) - tsconfig_plugin = PluginManager.get_responsible_plugin( - plugins, "path/to/tsconfig.json" - ) - poetry_plugin = PluginManager.get_responsible_plugin( - plugins, "path/to/pyproject.toml" - ) - spring_plugin = PluginManager.get_responsible_plugin( - plugins, "path/to/application.properties" - ) - apache_webserver_plugin = PluginManager.get_responsible_plugin( - plugins, "path/to/httpd.conf" - ) + docker_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/Dockerfile") + maven_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/pom.xml") + nodejs_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/package.json") + docker_compose_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/docker-compose.yml") + travis_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/.travis.yml") + cypress_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/cypress.json") + tsconfig_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/tsconfig.json") + poetry_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/pyproject.toml") + spring_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/application.properties") + apache_webserver_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/httpd.conf") mysql_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/my.cnf") ansible_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/ansible.cfg") ansible_playbook_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/playbooks/test.yaml") @@ -72,6 +52,7 @@ def test_get_responsible_plugin(): angular_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/angular.json") mapreduce_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/mapred-site.xml") circleci_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/.circleci/config.yml") + cargo_plugin = PluginManager.get_responsible_plugin(plugins, "path/to/Cargo.toml") assert docker_plugin.concept_name == "docker" assert maven_plugin.concept_name == "maven" @@ -100,3 +81,4 @@ def test_get_responsible_plugin(): assert angular_plugin.concept_name == "angular" assert mapreduce_plugin.concept_name == "mapreduce" assert circleci_plugin.concept_name == "circleci" + assert cargo_plugin.concept_name == "cargo" diff --git a/tests/files/Cargo.toml b/tests/files/Cargo.toml new file mode 100644 index 0000000..8948786 --- /dev/null +++ b/tests/files/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "example" +version = "0.1.0" +authors = ["Your Name "] +edition = "2021" +homepage = "https://example.com" +workspace = "path/to/workspace/root" + +# Optional description and license fields +description = "A simple Rust project example" +license = "MIT" + +[dependencies] +serde = "1.0" +rand = "0.8" + +# Optional section for dev dependencies, only used during development +[dev-dependencies] +tokio = { version = "1", features = ["full"] } + +[features] +default = ["serde"] + +# Optional section for custom configurations +[profile.release] +opt-level = 3 \ No newline at end of file