MIL-002 NATO converter: modelling, converter code, tests and Doxygen comments

User story, use case and SSD for "spell a word"; spell_word with
try/except KeyError, generate_phonetic with the retry loop, console and CSV
adapters, 49 pytest tests; MIL-002 and the modelling documents accepted with
review records.

Task: MIL-002#1
Task: MIL-002#2
Task: MIL-002#3
Task: MIL-002#4
Task: MIL-002#5
Task: MIL-002#6
This commit is contained in:
2026-10-14 19:35:08 +08:00
parent 12a5cb5562
commit 80ec1bc364
27 changed files with 1071 additions and 2 deletions
@@ -0,0 +1 @@
"""@brief Use case and ports of the NATO alphabet tool."""
@@ -0,0 +1,26 @@
"""@brief Use case: ask for a word until it can be spelled."""
from collections.abc import Mapping
from nato_phonetic_alphabet.application.ports import WordConsole
from nato_phonetic_alphabet.domain.spelling import InvalidWordError, spell_word
def generate_phonetic(alphabet: Mapping[str, str], console: WordConsole) -> list[str]:
"""@brief Ask for a word until it is valid, then return its code words.
This is the assignment's `generate_phonetic`: it wraps the input and the
error handling. An invalid word is reported to the user and asked for
again, so the function returns only for a valid word.
@param alphabet Code word per upper-case letter.
@param console Conversation with the user.
@return The code words of the word the user typed.
@throws InputClosedError The user can give no more input.
"""
while True:
word = console.ask_word()
try:
return spell_word(word, alphabet)
except InvalidWordError:
console.show_invalid()
@@ -0,0 +1,30 @@
"""@brief Ports the use case needs from the outside world."""
from typing import Protocol
class InputClosedError(Exception):
"""@brief Raised when the user can give no more input (end of input, abort)."""
class WordConsole(Protocol):
"""@brief Port for the conversation with the user."""
def ask_word(self) -> str:
"""@brief Ask the user for a word.
@return The word as typed.
@throws InputClosedError The user can give no more input.
"""
...
def show_invalid(self) -> None:
"""@brief Tell the user that the word was rejected."""
...
def show_code_words(self, code_words: list[str]) -> None:
"""@brief Show the result to the user.
@param code_words The code words to show.
"""
...