use super::args::RememberArgs;
use crate::constants::{MAX_MEMORY_DESCRIPTION_LEN, MAX_MEMORY_NAME_LEN};
use crate::errors::AppError;
pub(super) struct ResolvedName {
pub(super) original: String,
pub(super) normalized: String,
pub(super) was_normalized: bool,
}
pub(super) fn resolve(args: &RememberArgs) -> Result<ResolvedName, AppError> {
let original_name = args
.name_positional
.clone()
.or_else(|| args.name.clone())
.ok_or_else(|| {
AppError::Validation(crate::i18n::validation::name_required_positional_or_flag())
})?;
let normalized_name = {
let lower = original_name.to_lowercase().replace(['_', ' '], "-");
let trimmed = lower.trim_matches('-').to_string();
if trimmed != original_name {
tracing::warn!(target: "remember",
original = %original_name,
normalized = %trimmed,
"name auto-normalized to kebab-case"
);
}
trimmed
};
let name_was_normalized = normalized_name != original_name;
if args.strict_name && name_was_normalized {
return Err(AppError::Validation(
crate::i18n::validation::strict_name_not_canonical(&original_name, &normalized_name),
));
}
if normalized_name.is_empty() {
return Err(AppError::Validation(
crate::i18n::validation::name_empty_after_normalization(),
));
}
if normalized_name.len() > MAX_MEMORY_NAME_LEN {
return Err(AppError::LimitExceeded(
crate::i18n::validation::name_length(MAX_MEMORY_NAME_LEN),
));
}
if normalized_name.starts_with("__") {
return Err(AppError::Validation(
crate::i18n::validation::reserved_name(),
));
}
{
let slug_re = crate::constants::name_slug_regex();
if !slug_re.is_match(&normalized_name) {
return Err(AppError::Validation(crate::i18n::validation::name_kebab(
&normalized_name,
)));
}
}
if let Some(ref desc) = args.description {
if desc.len() > MAX_MEMORY_DESCRIPTION_LEN {
return Err(AppError::Validation(
crate::i18n::validation::description_exceeds(MAX_MEMORY_DESCRIPTION_LEN),
));
}
}
Ok(ResolvedName {
original: original_name,
normalized: normalized_name,
was_normalized: name_was_normalized,
})
}