yoagent_state/
observer.rs1use crate::{ArtifactRef, NodeId, PatchId, ProjectRef, StateOp};
2use serde_json::json;
3use std::path::Path;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct ChangedFile {
7 pub path: String,
8 pub status: String,
9}
10
11pub fn parse_git_name_status(raw: &str) -> Vec<ChangedFile> {
12 raw.lines()
13 .filter_map(|line| {
14 let mut parts = line.split_whitespace();
15 let status = parts.next()?;
16 let path = parts.next()?;
17 Some(ChangedFile {
18 path: path.to_string(),
19 status: status.to_string(),
20 })
21 })
22 .collect()
23}
24
25pub fn diff_artifact(
26 diff_path: impl AsRef<Path>,
27 summary: impl Into<String>,
28 base_commit: impl Into<String>,
29 files: &[ChangedFile],
30) -> ArtifactRef {
31 ArtifactRef::new(
32 "git.diff",
33 format!("file://{}", diff_path.as_ref().display()),
34 )
35 .with_summary(summary)
36 .with_metadata(json!({
37 "base_commit": base_commit.into(),
38 "files_changed": files.iter().map(|file| &file.path).collect::<Vec<_>>(),
39 }))
40}
41
42pub fn project_ref(
43 repo: impl Into<String>,
44 branch: Option<String>,
45 commit: Option<String>,
46 worktree: Option<String>,
47) -> ProjectRef {
48 ProjectRef {
49 repo: repo.into(),
50 branch,
51 commit,
52 worktree,
53 }
54}
55
56pub fn changed_file_ops(patch_id: PatchId, files: &[ChangedFile]) -> Vec<StateOp> {
57 let patch_node_id = NodeId::new(patch_id.0);
58 let mut ops = Vec::new();
59 for file in files {
60 let file_id = NodeId::new(format!("file:{}", file.path));
61 ops.push(StateOp::CreateNode {
62 id: file_id.clone(),
63 kind: "file".to_string(),
64 props: json!({ "path": file.path, "status": file.status }),
65 });
66 ops.push(StateOp::CreateRelation {
67 from: patch_node_id.clone(),
68 rel: "modifies".to_string(),
69 to: file_id,
70 props: json!({ "status": file.status }),
71 });
72 }
73 ops
74}