chronicle/annotate/
gather.rs1use crate::error::{chronicle_error, Result};
2use crate::git::{FileDiff, GitOps};
3use snafu::ResultExt;
4use std::path::PathBuf;
5
6#[derive(Debug, Clone, Default)]
8pub struct AuthorContext {
9 pub task: Option<String>,
10 pub reasoning: Option<String>,
11 pub dependencies: Option<String>,
12 pub tags: Vec<String>,
13}
14
15#[derive(Debug, Clone)]
17pub struct AnnotationContext {
18 pub commit_sha: String,
19 pub commit_message: String,
20 pub author_name: String,
21 pub author_email: String,
22 pub timestamp: String,
23 pub diffs: Vec<FileDiff>,
24 pub author_context: Option<AuthorContext>,
25}
26
27pub fn build_context(git_ops: &dyn GitOps, commit: &str) -> Result<AnnotationContext> {
29 let info = git_ops
31 .commit_info(commit)
32 .context(chronicle_error::GitSnafu)?;
33
34 let diffs = git_ops.diff(commit).context(chronicle_error::GitSnafu)?;
36
37 let author_context = gather_author_context();
39
40 Ok(AnnotationContext {
41 commit_sha: info.sha,
42 commit_message: info.message,
43 author_name: info.author_name,
44 author_email: info.author_email,
45 timestamp: info.timestamp,
46 diffs,
47 author_context,
48 })
49}
50
51fn gather_author_context() -> Option<AuthorContext> {
53 let pending = read_pending_context_from_git_dir();
55
56 let env_task = std::env::var("CHRONICLE_TASK")
58 .ok()
59 .filter(|s| !s.is_empty());
60 let env_reasoning = std::env::var("CHRONICLE_REASONING")
61 .ok()
62 .filter(|s| !s.is_empty());
63 let env_dependencies = std::env::var("CHRONICLE_DEPENDENCIES")
64 .ok()
65 .filter(|s| !s.is_empty());
66 let env_tags: Vec<String> = std::env::var("CHRONICLE_TAGS")
67 .ok()
68 .filter(|s| !s.is_empty())
69 .map(|s| s.split(',').map(|t| t.trim().to_string()).collect())
70 .unwrap_or_default();
71
72 let mut ctx = pending.map(|p| p.to_author_context()).unwrap_or_default();
74
75 if env_task.is_some() {
76 ctx.task = env_task;
77 }
78 if env_reasoning.is_some() {
79 ctx.reasoning = env_reasoning;
80 }
81 if env_dependencies.is_some() {
82 ctx.dependencies = env_dependencies;
83 }
84 if !env_tags.is_empty() {
85 ctx.tags = env_tags;
86 }
87
88 if ctx.task.is_none()
90 && ctx.reasoning.is_none()
91 && ctx.dependencies.is_none()
92 && ctx.tags.is_empty()
93 {
94 None
95 } else {
96 Some(ctx)
97 }
98}
99
100fn read_pending_context_from_git_dir() -> Option<crate::hooks::PendingContext> {
102 let git_dir = PathBuf::from(".git");
104 if !git_dir.exists() {
105 return None;
106 }
107 crate::hooks::read_pending_context(&git_dir).ok().flatten()
108}