Metadata-Version: 2.4
Name: chars-counter
Version: 0.4.0
Summary: Chars counter console util
Author: Alexander Zakorko
Author-email: Alexander Zakorko <alex.zakorko2015@gmail.com>
License-Expression: MIT
Requires-Dist: click>=8.4.1
Requires-Dist: levenshtein>=0.27.3
Requires-Python: >=3.14
Description-Content-Type: text/markdown

# chars-counter

A Python utility that counts, per ASCII category, how many characters appear **exactly once** in a given string. Works as both a CLI tool and an importable library.

**Requires Python ≥ 3.14**

---

## Installation

```bash
pip install chars-counter
```

```bash
uv add chars-counter
```

---

## CLI usage

Run the module directly with `python -m chars_counter`.

### Count characters in a string

```bash
python -m chars_counter --string "Hello"
# Counter({<ASCIICharCategories.Lowercase_Letters: 'lowercase_letters'>: 2, <ASCIICharCategories.Uppercase_Letters: 'uppercase_letters'>: 1})
```

`H` appears once → 1 uppercase. `e` and `o` each appear once → 2 lowercase. `l` appears twice → ignored.

### Count characters from a file

```bash
python -m chars_counter --file path/to/file.txt
```

### Help

```bash
python -m chars_counter --help
```

### Typo suggestions

Mistyped flags are caught and the closest valid flag is suggested:

```bash
python -m chars_counter --flie notes.txt
# --flie cannot be used, maybe you meant: --file
```

---

## Library usage

### Basic example

```python
from chars_counter import get_once_seen_chars_categories_count, ASCIICharCategories

result = get_once_seen_chars_categories_count("Hello")
# Counter({<ASCIICharCategories.Lowercase_Letters: 'lowercase_letters'>: 2,
#          <ASCIICharCategories.Uppercase_Letters: 'uppercase_letters'>: 1})

result[ASCIICharCategories.Lowercase_Letters]  # 2
result[ASCIICharCategories.Uppercase_Letters]  # 1
```

### Iterating over results

```python
from chars_counter import get_once_seen_chars_categories_count

result = get_once_seen_chars_categories_count("aA1")

for category, count in result.items():
    print(f"{category}: {count}")
# lowercase_letters: 1
# uppercase_letters: 1
# digits: 1
```

### Empty / all-duplicate strings

Strings where every character appears more than once return an empty dict:

```python
get_once_seen_chars_categories_count("aabb")  # Counter()
get_once_seen_chars_categories_count("")      # Counter()
```

### Handling non-ASCII input

Passing a string that contains non-ASCII characters raises `NotAsciiCharacterProvidedError`:

```python
from chars_counter import get_once_seen_chars_categories_count
from chars_counter.not_ascii_character_provided_error import NotAsciiCharacterProvidedError

try:
    get_once_seen_chars_categories_count("café")
except NotAsciiCharacterProvidedError as e:
    print(e.char)      # 'é'
    print(e.position)  # 3
    print(e)           # Non-ASCII character 'é' found at position 3.
```

---

## ASCII character categories

| Category             | Code points                               | Examples       |
|----------------------|-------------------------------------------|----------------|
| `Control_Characters` | 0–31, 127                                 | `\t`, `\n`     |
| `Space`              | 32                                        | ` `            |
| `Punctuation`        | 33–47, 58–64, 91–96, 123–126             | `!`, `.`, `@`  |
| `Digits`             | 48–57                                     | `0`–`9`        |
| `Uppercase_Letters`  | 65–90                                     | `A`–`Z`        |
| `Lowercase_Letters`  | 97–122                                    | `a`–`z`        |

---

## Public API reference

### `get_once_seen_chars_categories_count(string: str) -> dict[ASCIICharCategories, int]`

Returns a `Counter` mapping each `ASCIICharCategories` member to the number of characters in that category that appear exactly once in `string`. Categories with no once-seen characters are omitted.

Results are cached (`lru_cache`, capacity 1000).

Raises `NotAsciiCharacterProvidedError` if `string` contains any non-ASCII character.

### `ASCIICharCategories`

A `StrEnum` with members: `Control_Characters`, `Space`, `Punctuation`, `Digits`, `Uppercase_Letters`, `Lowercase_Letters`.

Each member's string value is its snake_case name (e.g. `ASCIICharCategories.Lowercase_Letters == "lowercase_letters"`).

### `NotAsciiCharacterProvidedError`

Inherits from `Exception`. Attributes:
- `char: str` — the offending character
- `position: int` — its index in the input string

---

## Development

```bash
uv sync
uv run pytest
uv run coverage run -m pytest && uv run coverage report
uv run ruff format 
uv run ruff check --fix
uv run pyright
```