diff --git a/ChangeLog.md b/ChangeLog.md index eaa36b8..78edcaf 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -8,6 +8,12 @@ ([#10](https://github.com/davep/gophermap/pull/10)) - Added an optional strict mode (and, in doing so, added an exception back). ([#12](https://github.com/davep/gophermap/pull/12)) +- Added `has_error` instance property to the `GopherMap` class. + ([#13](https://github.com/davep/gophermap/pull/13)) +- Added `is_likely_a_map` as a class method. + ([#13](https://github.com/davep/gophermap/pull/13)) +- Added `is_likely_error` as a class method. + ([#13](https://github.com/davep/gophermap/pull/13)) ## v0.2.0 diff --git a/src/gophermap/gopher_map.py b/src/gophermap/gopher_map.py index 3e64ab7..89beb57 100644 --- a/src/gophermap/gopher_map.py +++ b/src/gophermap/gopher_map.py @@ -8,8 +8,9 @@ ############################################################################## # Local imports. -from .exceptions import EmptyMap +from .exceptions import EmptyMap, GopherMapError from .item import GopherItem +from .item_type import ItemType ############################################################################## EOF: Final[str] = "." @@ -64,5 +65,53 @@ def items(self) -> tuple[GopherItem, ...]: """ return tuple(self._parse_map(self._raw)) + @cached_property + def has_error(self) -> bool: + """Whether the Gopher map contains an error item. + + Raises: + EmptyMap: If the map is empty and strict mode is enabled. + NoFields: If the line is missing a tab character and strict mode is enabled. + UnknownItemType: If the item type is unknown and strict mode is enabled. + + Note: + The mentioned exceptions will be raised on accessing this + property, if in strict mode. If you wish to check for errors + without raising exceptions, use the [`is_likely_error` class + method][gophermap.GopherMap.is_likely_error], to test instead. + """ + return any(item.type is ItemType.ERROR for item in self.items) + + @classmethod + def is_likely_a_map(cls, text: str) -> bool: + """Determine if the text is likely a valid Gopher map. + + Args: + text: The text of the Gopher map. + + Returns: + True if the text is likely a valid Gopher map, False otherwise. + """ + try: + _ = cls(text, strict=True).items + except GopherMapError: + return False + return True + + @classmethod + def is_likely_error(cls, text: str) -> bool: + """Determine if the text is likely a Gopher error message. + + Args: + text: The text to test. + + Returns: + True if the text is likely a Gopher error message, False otherwise. + """ + try: + return cls(text, strict=True).has_error + except GopherMapError: + return False + ### gopher_map.py ends here diff --git a/tests/test_gopher_map.py b/tests/test_gopher_map.py index 1741cfd..4f8a9d0 100644 --- a/tests/test_gopher_map.py +++ b/tests/test_gopher_map.py @@ -86,4 +86,58 @@ def test_strict_on_bad_map(test_map: str) -> None: _ = GopherMap(test_map, strict=True).items +############################################################################## +@mark.parametrize( + "test_map, expected", + [ + ("iHello\tworld\tlocalhost\t70\r\n3Error\tworld\tlocalhost\t70\r\n.\r\n", True), + ("iHello\tworld\tlocalhost\t70\r\n.\r\n", False), + ("3Error\tworld\tlocalhost\t70\r\n.\r\n", True), + ("This is just some random test", False), + (".\r\n", False), + ("", False), + ], +) +def test_has_error(test_map: str, expected: bool) -> None: + """Test that has_error returns True if the map contains an error item.""" + assert GopherMap(test_map).has_error is expected + + +############################################################################## +@mark.parametrize( + "test_map, expected", + [ + ("", False), + ("iHello\tworld\tlocalhost\t70\r\n3Error\tworld\tlocalhost\t70\r\n.\r\n", True), + ("iHello\tworld\tlocalhost\t70\r\n.\r\n", True), + ("3Error\tworld\tlocalhost\t70\r\n.\r\n", True), + ("This is just some random test", False), + ("Hello\tworld\tlocalhost\t70\r\n.\r\n", False), + ("hello\tworld\tlocalhost\t70\r\n.\r\n", True), + (".\r\n", True), + ], +) +def test_is_likel_a_map(test_map: str, expected: bool) -> None: + """Test is_likely_a_map returns True if the map is likely a valid Gopher map.""" + assert GopherMap.is_likely_a_map(test_map) is expected + + +############################################################################## +@mark.parametrize( + "test_map, expected", + [ + ("iHello\tworld\tlocalhost\t70\r\n3Error\tworld\tlocalhost\t70\r\n.\r\n", True), + ("iHello\tworld\tlocalhost\t70\r\n.\r\n", False), + ("3Error\tworld\tlocalhost\t70\r\n.\r\n", True), + ("This is just some random test", False), + ("3. This is just some random test\r\n.\r\n", False), + (".\r\n", False), + ("", False), + ], +) +def test_is_likely_error(test_map: str, expected: bool) -> None: + """Test that is_likely_error returns True if the map contains an error item.""" + assert GopherMap.is_likely_error(test_map) is expected + + ### test_gopher_map.py ends here