pub use crate::error::SourceError;
use std::path::PathBuf;
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum SourceType {
GitHub { owner: String, repo: String },
FileSystem { path: PathBuf },
Stdin,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct SourceContent {
pub source_type: SourceType,
pub content: String,
pub metadata: std::collections::HashMap<String, String>,
}
pub struct SourceResolver;
impl SourceResolver {
pub fn resolve_github(
owner: &str,
repo: &str,
issue_id: &str,
) -> Result<SourceContent, SourceError> {
if owner.is_empty() || repo.is_empty() {
return Err(SourceError::InvalidFormat {
reason: "GitHub owner and repo must be specified".to_string(),
});
}
if issue_id.parse::<u32>().is_err() {
return Err(SourceError::InvalidFormat {
reason: "Issue ID must be a valid number".to_string(),
});
}
let content = format!(
"GitHub issue #{issue_id} from {owner}/{repo}\n\nThis is a simulated issue description that would be fetched from the GitHub API."
);
let mut metadata = std::collections::HashMap::new();
metadata.insert("owner".to_string(), owner.to_string());
metadata.insert("repo".to_string(), repo.to_string());
metadata.insert("issue_id".to_string(), issue_id.to_string());
Ok(SourceContent {
source_type: SourceType::GitHub {
owner: owner.to_string(),
repo: repo.to_string(),
},
content,
metadata,
})
}
pub fn resolve_filesystem(path: &PathBuf) -> Result<SourceContent, SourceError> {
if !path.exists() {
return Err(SourceError::FileSystemNotFound {
path: path.display().to_string(),
});
}
let content = if path.is_file() {
std::fs::read_to_string(path).map_err(|_| SourceError::FileSystemNotFound {
path: path.display().to_string(),
})?
} else if path.is_dir() {
format!(
"Directory source: {}\n\nThis would contain a summary of the directory contents and relevant files.",
path.display()
)
} else {
return Err(SourceError::FileSystemNotFound {
path: path.display().to_string(),
});
};
let mut metadata = std::collections::HashMap::new();
metadata.insert("path".to_string(), path.display().to_string());
metadata.insert(
"type".to_string(),
if path.is_file() { "file" } else { "directory" }.to_string(),
);
Ok(SourceContent {
source_type: SourceType::FileSystem { path: path.clone() },
content,
metadata,
})
}
pub fn resolve_stdin() -> Result<SourceContent, SourceError> {
use std::io::Read;
let mut buffer = String::new();
std::io::stdin()
.read_to_string(&mut buffer)
.map_err(|e| SourceError::StdinReadFailed {
reason: e.to_string(),
})?;
if buffer.trim().is_empty() {
return Err(SourceError::EmptyInput);
}
let mut metadata = std::collections::HashMap::new();
metadata.insert("length".to_string(), buffer.len().to_string());
Ok(SourceContent {
source_type: SourceType::Stdin,
content: buffer,
metadata,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::UserFriendlyError;
#[test]
fn test_github_source_resolution() {
let result = SourceResolver::resolve_github("owner", "repo", "123");
assert!(result.is_ok());
let content = result.unwrap();
assert!(content.content.contains("GitHub issue #123"));
assert_eq!(content.metadata.get("owner"), Some(&"owner".to_string()));
}
#[test]
fn test_github_source_invalid_issue() {
let result = SourceResolver::resolve_github("owner", "repo", "invalid");
assert!(result.is_err());
if let Err(SourceError::InvalidFormat { reason }) = result {
assert!(reason.contains("valid number"));
} else {
panic!("Expected InvalidFormat error");
}
}
#[test]
fn test_filesystem_source_not_found() {
let path = PathBuf::from("__nonexistent_test_path_7f8e9d6c5b4a3__");
let result = SourceResolver::resolve_filesystem(&path);
assert!(result.is_err());
if let Err(SourceError::FileSystemNotFound { path: error_path }) = result {
assert!(error_path.contains("nonexistent"));
} else {
panic!("Expected FileSystemNotFound error");
}
}
#[test]
fn test_source_error_user_friendly_messages() {
let error = SourceError::GitHubAuthFailed {
reason: "authentication failed".to_string(),
};
assert!(error.user_message().contains("authentication failed"));
assert!(!error.suggestions().is_empty());
assert!(error.suggestions().iter().any(|s| s.contains("auth")));
}
}