use super::{git_repository::GitRepository, CodeChunk, DifficultyLevel};
use std::borrow::Cow;
use std::path::Path;
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct Challenge {
pub id: String,
pub source_file_path: Option<String>,
pub code_content: String,
pub start_line: Option<usize>,
pub end_line: Option<usize>,
pub language: Option<String>,
pub comment_ranges: Vec<(usize, usize)>, pub difficulty_level: Option<DifficultyLevel>,
}
impl Challenge {
pub fn new(id: String, code_content: String) -> Self {
Self {
id,
source_file_path: None,
code_content,
start_line: None,
end_line: None,
language: None,
comment_ranges: Vec::new(),
difficulty_level: None,
}
}
pub fn with_source_info(
mut self,
file_path: String,
start_line: usize,
end_line: usize,
) -> Self {
self.source_file_path = Some(file_path);
self.start_line = Some(start_line);
self.end_line = Some(end_line);
self
}
pub fn with_language(mut self, language: String) -> Self {
self.language = Some(language);
self
}
pub fn with_comment_ranges(mut self, comment_ranges: Vec<(usize, usize)>) -> Self {
self.comment_ranges = comment_ranges;
self
}
pub fn with_difficulty_level(mut self, difficulty_level: DifficultyLevel) -> Self {
self.difficulty_level = Some(difficulty_level);
self
}
pub fn from_chunk(chunk: &CodeChunk, difficulty: Option<DifficultyLevel>) -> Option<Self> {
use uuid::Uuid;
if chunk.content.trim().is_empty() {
return None;
}
let id = Uuid::new_v4().to_string();
let code_content = chunk.content.clone();
let source_file_path = Some(chunk.file_path.to_string_lossy().to_string());
let start_line = Some(chunk.start_line);
let end_line = Some(chunk.end_line);
let language = Some(chunk.language.clone());
Some(Self {
id,
code_content,
source_file_path,
start_line,
end_line,
language,
difficulty_level: difficulty,
comment_ranges: chunk.comment_ranges.clone(),
})
}
pub fn from_content_and_chunk(
content: Cow<str>,
chunk: &crate::domain::models::CodeChunk,
start_line: usize,
end_line: usize,
comment_ranges: &[(usize, usize)],
difficulty: Option<DifficultyLevel>,
) -> Self {
use uuid::Uuid;
let id = Uuid::new_v4().to_string();
let code_content = content.into_owned();
let source_file_path = Some(chunk.file_path.to_string_lossy().to_string());
let language = Some(chunk.language.clone());
Self {
id,
code_content,
source_file_path,
start_line: Some(start_line),
end_line: Some(end_line),
language,
difficulty_level: difficulty,
comment_ranges: comment_ranges.to_vec(),
}
}
pub fn get_display_title(&self) -> String {
if let Some(ref path) = self.source_file_path {
let relative_path = self.get_relative_path(path);
if let (Some(start), Some(end)) = (self.start_line, self.end_line) {
format!("{}:{}-{}", relative_path, start, end)
} else {
relative_path
}
} else {
format!("Challenge {}", self.id)
}
}
pub fn get_display_title_with_repo(&self, repo_info: &Option<GitRepository>) -> String {
if let Some(ref path) = self.source_file_path {
let relative_path = self.get_relative_path(path);
let file_info = if let (Some(start), Some(end)) = (self.start_line, self.end_line) {
format!("{}:{}-{}", relative_path, start, end)
} else {
relative_path
};
if let Some(repo) = repo_info {
format!(
"[{}/{}] {}",
repo.user_name, repo.repository_name, file_info
)
} else {
file_info
}
} else {
format!("Challenge {}", self.id)
}
}
fn get_relative_path(&self, path: &str) -> String {
if let Some(file_name) = Path::new(path).file_name() {
if let Some(parent) = Path::new(path).parent() {
if let Some(parent_name) = parent.file_name() {
format!(
"{}/{}",
parent_name.to_string_lossy(),
file_name.to_string_lossy()
)
} else {
file_name.to_string_lossy().to_string()
}
} else {
file_name.to_string_lossy().to_string()
}
} else {
path.to_string()
}
}
}