# Copyright 2026 European Union
# Author: Bulgheroni Antonio (antonio.bulgheroni@ec.europa.eu)
# SPDX-License-Identifier: EUPL-1.2
"""Interactive prompts and success reporting for project scaffolding.
:author: Bulgheroni Antonio
"""
import pathlib
from rich import print as rprint
from rich.prompt import Prompt
from mafw.scaffold import ProjectSpec, ScaffoldError
from mafw.scaffold.names import to_import_name, validate, validate_email
[docs]
def _prompt_project_name(project_name: str | None) -> str:
"""Prompt for a valid project name, re-prompting up to three times.
:param project_name: The name supplied on the command line, if any.
:type project_name: str | None
:return: A validated project name.
:rtype: str
:raises mafw.scaffold.errors.ScaffoldError: If a valid name is not provided within the attempts.
"""
attempts = 0
candidate = project_name
while True:
if candidate is not None and candidate.strip():
try:
validate(candidate.strip(), to_import_name(candidate.strip()))
return candidate.strip()
except ScaffoldError as exc:
rprint(f'[yellow]{exc}[/yellow]')
attempts += 1
if attempts > 3:
raise ScaffoldError('No valid project name was provided after several attempts.')
candidate = Prompt.ask('Project name')
[docs]
def _prompt_optional(label: str, value: str | None, default: str) -> str:
"""Prompt for an optional value, showing and accepting a default.
:param label: The human-readable label for the prompt.
:type label: str
:param value: The value supplied on the command line, if any.
:type value: str | None
:param default: The default used when the user accepts the prompt.
:type default: str
:return: The resolved value.
:rtype: str
"""
if value is not None and value.strip():
return value.strip()
return str(Prompt.ask(label, default=default))
[docs]
def _prompt_author_email(value: str | None, default: str) -> str:
"""Prompt for a well-formed author e-mail, re-prompting up to three times.
A malformed e-mail would make the generated project's build backend fail, so
the address is validated here and the user is asked again on invalid input.
:param value: The e-mail supplied on the command line, if any.
:type value: str | None
:param default: The default e-mail used when the user accepts the prompt.
:type default: str
:return: A validated e-mail address.
:rtype: str
:raises mafw.scaffold.errors.ScaffoldError: If no valid e-mail is provided in time.
"""
attempts = 0
candidate = value
while True:
if candidate is not None and candidate.strip():
try:
validate_email(candidate.strip())
return candidate.strip()
except ScaffoldError as exc:
rprint(f'[yellow]{exc}[/yellow]')
attempts += 1
if attempts > 3:
raise ScaffoldError('No valid author e-mail was provided after several attempts.')
candidate = str(Prompt.ask('Author e-mail', default=default))
[docs]
def _report_success(spec: ProjectSpec, project_dir: pathlib.Path, install: str | None) -> None:
"""Print the post-generation summary and next-step commands.
:param spec: The validated project specification.
:type spec: mafw.scaffold.spec.ProjectSpec
:param project_dir: The absolute path to the generated project.
:type project_dir: pathlib.Path
:param install: The install manager requested, if any.
:type install: str | None
"""
steering = f'conf/{spec.import_name}_steering.toml'
rprint(f'[green]Project created:[/green] [blue underline]{project_dir}[/blue underline]')
rprint(f' Distribution name: [cyan]{spec.distribution_name}[/cyan]')
rprint(f' Import package: [cyan]{spec.import_name}[/cyan]')
rprint('')
rprint('Next steps:')
rprint(f' cd {project_dir}')
if install is None:
rprint(' hatch env create')
rprint(' hatch run mafw list')
rprint(f' hatch run mafw run {steering}')
rprint(' hatch test')