use crate::error::DevbrainError;
use chrono::{DateTime, SecondsFormat, Utc};
use std::env;
const MAX_INPUT_LENGTH: usize = 500;
pub fn get_timestamp() -> String {
Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true)
}
pub fn format_timestamp(timestamp: &str) -> String {
DateTime::parse_from_rfc3339(timestamp)
.map(|timestamp| timestamp.format("%Y-%m-%d %H:%M").to_string())
.unwrap_or_else(|_| timestamp.to_string())
}
pub fn get_project_name() -> String {
env::current_dir()
.ok()
.and_then(|path| {
path.file_name()
.map(|name| name.to_string_lossy().into_owned())
})
.filter(|name| !name.is_empty())
.unwrap_or_else(|| "unknown".to_string())
}
pub fn normalize_input(input: &str) -> Result<String, DevbrainError> {
let normalized = input.trim();
if normalized.is_empty() {
return Err(DevbrainError::Other("Input cannot be empty".to_string()));
}
if normalized.chars().count() > MAX_INPUT_LENGTH {
return Err(DevbrainError::Other("Input too long".to_string()));
}
Ok(normalized.to_string())
}
pub fn normalize_command_input(input: &str) -> Result<String, DevbrainError> {
let collapsed = input.split_whitespace().collect::<Vec<_>>().join(" ");
normalize_input(&collapsed)
}
pub fn normalize_tags(tags: &[String]) -> Vec<String> {
tags.iter()
.map(|tag| tag.trim().to_lowercase())
.filter(|tag| !tag.is_empty())
.collect()
}
pub fn infer_tags(content: &str) -> Vec<String> {
let content = content.to_lowercase();
let tokens: Vec<&str> = content
.split(|c: char| !c.is_alphanumeric())
.filter(|token| !token.is_empty())
.collect();
let has_token = |needle: &str| tokens.contains(&needle);
let mut tags = Vec::new();
if content.contains("auth") {
tags.push("auth".to_string());
}
if has_token("db") || content.contains("database") {
tags.push("database".to_string());
}
if has_token("api") {
tags.push("api".to_string());
}
tags
}