diff --git a/CHANGELOG b/CHANGELOG index 0ec74209..aa37d246 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,6 +17,7 @@ jc changelog - Add source link to online parser documentation - Add snap package build scripts - Add `remove_quotes` function to `utils.py` +- Add `normalize_key` function to `utils.py` - Add `line_slice` function to `utils.py` - Add `get_parser` function to `lib.py` - Enhance `nsd-control` parser to support more zone information diff --git a/docs/utils.md b/docs/utils.md index 56173cea..30899517 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -7,6 +7,7 @@ * [compatibility](#jc.utils.compatibility) * [has\_data](#jc.utils.has_data) * [remove\_quotes](#jc.utils.remove_quotes) + * [normalize\_key](#jc.utils.normalize_key) * [convert\_to\_int](#jc.utils.convert_to_int) * [convert\_to\_float](#jc.utils.convert_to_float) * [convert\_to\_bool](#jc.utils.convert_to_bool) @@ -134,6 +135,32 @@ def remove_quotes(data: str) -> str Remove single or double quotes surrounding a string. If no quotes are found then the string is returned unmodified. +Parameters: + + data: (string) Input value + +Returns: + + string + + + +### normalize\_key + +```python +def normalize_key(data: str) -> str +``` + +Normalize a key name by shifting to lower-case and converting special +characters to underscores. + +Special characters are defined as `space` and the following: + + !"#$%&'()*+,-./:;<=>?@[\]^`{|}~ + +This is a lossy algorithm. Repeating and trailing underscores are +removed. + Parameters: data: (string) Input value diff --git a/jc/utils.py b/jc/utils.py index fe3736bd..9ee5e765 100644 --- a/jc/utils.py +++ b/jc/utils.py @@ -205,6 +205,46 @@ def remove_quotes(data: str) -> str: return data +def normalize_key(data: str) -> str: + """ + Normalize a key name by shifting to lower-case and converting special + characters to underscores. + + Special characters are defined as `space` and the following: + + !"#$%&'()*+,-./:;<=>?@[\]^`{|}~ + + This is a lossy algorithm. Repeating and trailing underscores are + removed. + + Parameters: + + data: (string) Input value + + Returns: + + string + """ + special = '''!"#$%&'()*+,-./:;<=>?@[\]^`{|}~ ''' + initial_underscore = False + data = data.strip().lower() + + for special_char in special: + data = data.replace(special_char, '_') + + if data.startswith('_'): + initial_underscore = True + + # swap back to space so split() will compress multiple consecutive down to one + data = data.strip('_').replace('_', ' ') + data = '_'.join(data.split()) + + if initial_underscore: + data = '_' + data + + return data + + def convert_to_int(value: object) -> Optional[int]: """ Converts string and float input to int. Strips all non-numeric diff --git a/man/jc.1 b/man/jc.1 index ba7c089f..9c512aaa 100644 --- a/man/jc.1 +++ b/man/jc.1 @@ -1,4 +1,4 @@ -.TH jc 1 2024-02-01 1.24.1 "JSON Convert" +.TH jc 1 2024-02-02 1.24.1 "JSON Convert" .SH NAME \fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types, and strings diff --git a/tests/test_jc_utils.py b/tests/test_jc_utils.py index 0ed5dad1..f3f3fc1a 100644 --- a/tests/test_jc_utils.py +++ b/tests/test_jc_utils.py @@ -294,6 +294,25 @@ line5 expected = ['line2', '', 'line4'] self.assertEqual(list(jc.utils.line_slice(data, 1, 4)), expected) + def test_remove_quotes(self): + for char in ["'", '"']: + with self.subTest(f'Quote character: {char}'): + data = f'{char}this is a test{char}' + expected = 'this is a test' + self.assertEqual(jc.utils.remove_quotes(data), expected) + + def test_normalize_key(self): + io_map = { + 'This is @ crazy Key!!': 'this_is_crazy_key', + 'Simple': 'simple', + 'CamelCase': 'camelcase', + '^Complex-Key*': '_complex_key' + } + + for data, expected in io_map.items(): + with self.subTest(f'Original key: {data}'): + self.assertEqual(jc.utils.normalize_key(data), expected) + # need to mock shutil.get_terminal_size().columns or add a column parameter to test # def test_utils_warning_message(self):