# It all started with @click

and, well. It went on from there.

I'm going to post some code now.

```python
from collections.abc import Generator
from contextlib import contextmanager, suppress
from dataclasses import dataclass

import click
import requests
from beartype.typing import Any, Sequence, TypedDict, Unpack

from src.auth import fetch_auth_token
from src.settings import SETTINGS


class CliOptions(TypedDict):
    auth_type: str
    chat_type: str
    topic: str | None
    participant: str | None


@dataclass(eq=False)
class Options:
    auth_type: str
    chat_type: str
    topic: str | None
    participant: str | None


@contextmanager
def find_in_instance(
    cli_options, data
) -> Generator[list[dict[str, None | str | dict[str, str]]], Any, None]:
    match cli_options:
        case Options(participant=None, topic=None, chat_type=("oneOnOne" | "group" | "meeting")):
            yield [item for item in data if cli_options.chat_type == item["chatType"]]
        case Options(participant=None, chat_type=("oneOnOne" | "group" | "meeting")):
            yield [
                item
                for item in data
                if cli_options.chat_type == item["chatType"] and cli_options.topic in item["topic".lower()]
            ]
        case Options(topic=None, chat_type=("oneOnOne" | "group" | "meeting")):
            yield [
                item
                for item in data
                if cli_options.chat_type == item["chatType"]
                for member in item["members"]
                if cli_options.participant in member["displayName"].lower()
            ]
        case Options(chat_type=("oneOnOne" | "group" | "meeting")):
            yield [
                item
                for item in data
                if cli_options.chat_type == item["chatType"] and cli_options.topic in item["topic"]
                for member in item["members"]
                if cli_options.participant in member["displayName"]
            ]
        case __:
            yield [{"default": None}]


def lowercase(ctx, param, value):
    if value is not None:
        return value.lower()


@click.command("cli")
@click.option(
    "--auth-type",
    type=click.Choice(
        ["interactive", "code"],
        case_sensitive=False,
    ),
    default="interactive",
    help="Default is 'interactive'. If running headless use 'code'",
)
@click.option(
    "--chat-type",
    type=click.Choice(["oneOnOne", "group", "meeting"], case_sensitive=False),
    default="oneOnOne",
    help="Chat is either oneOnOne [default],  group or meeting.",
)
@click.option(
    "--topic",
    callback=lowercase,
    help="Filter on chat name or topic when it contains the <string>",
)
@click.option(
    "--participant",
    callback=lowercase,
    help="Filter on chat participants name when it contains the <string>",
)
def cli(**kwargs: Unpack[CliOptions]):
    def recursive_fetch(
        url: str,
        headers: dict[str, str],
        cli_options: Options,
        index: int = 0,
    ) -> int:
        ...do some processing
    ..do auth and some pre prossesing
    cli_options = Options(**kwargs)
    recursive_fetch(url, headers, cli_options)
```

So, what are we looking at here? We are looking at a client that fetches chats from https://graph.microsoft.com/v1.0/me/chats?$top=50&$expand=members, recursively.

It started when `ruff` complained I had too many parameters in my method call. So, naturally, I turned to `**kwargs` I usually never turn to `**kwargs` but with TypedDict I feel I can have a clear and concise understanding of what hides inside the `dict`. So that's one thing. I haven't seen this particular pattern used with click in the wild yet. I might be the only one with this set of needs... or I'm just very poor at designing command-line applications. That might be it.

Anyways. One thing led to another. This is the first time I've used the `match` statement with any success. I've tried it before but never had a use case that actually resulted in fewer lines and clearer code. Now I think I have.

I will post the auth and the Settings too - when I get around to doing it. For the time being, this post is somewhat of a placeholder so that I don't forget..
