1use git::repository::DiffType;
2use gpui::{App, Entity, Task};
3use serde::{Deserialize, Serialize};
4use worktree::Worktree;
5
6use crate::{
7 Project,
8 git_store::{GitStore, LocalRepositoryState, RepositoryState},
9};
10
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12pub struct TelemetrySnapshot {
13 pub worktree_snapshots: Vec<TelemetryWorktreeSnapshot>,
14}
15
16impl TelemetrySnapshot {
17 pub fn new(project: &Entity<Project>, cx: &mut App) -> Task<TelemetrySnapshot> {
18 let git_store = project.read(cx).git_store().clone();
19 let worktree_snapshots: Vec<_> = project
20 .read(cx)
21 .visible_worktrees(cx)
22 .map(|worktree| TelemetryWorktreeSnapshot::new(worktree, git_store.clone(), cx))
23 .collect();
24
25 cx.spawn(async move |_| {
26 let worktree_snapshots = futures::future::join_all(worktree_snapshots).await;
27
28 Self { worktree_snapshots }
29 })
30 }
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
34pub struct TelemetryWorktreeSnapshot {
35 pub worktree_path: String,
36 pub git_state: Option<GitState>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
40pub struct GitState {
41 pub remote_url: Option<String>,
42 pub head_sha: Option<String>,
43 pub current_branch: Option<String>,
44 pub diff: Option<String>,
45}
46
47impl TelemetryWorktreeSnapshot {
48 fn new(
49 worktree: Entity<Worktree>,
50 git_store: Entity<GitStore>,
51 cx: &App,
52 ) -> Task<TelemetryWorktreeSnapshot> {
53 cx.spawn(async move |cx| {
54 // Get worktree path and snapshot
55 let worktree_info = cx.update(|app_cx| {
56 let worktree = worktree.read(app_cx);
57 let path = worktree.abs_path().to_string_lossy().into_owned();
58 let snapshot = worktree.snapshot();
59 (path, snapshot)
60 });
61
62 let (worktree_path, _snapshot) = worktree_info;
63
64 let git_state = git_store
65 .update(cx, |git_store, cx| {
66 git_store
67 .repositories()
68 .values()
69 .find(|repo| {
70 repo.read(cx)
71 .abs_path_to_repo_path(&worktree.read(cx).abs_path())
72 .is_some()
73 })
74 .cloned()
75 })
76 .map(|repo| {
77 repo.update(cx, |repo, _| {
78 let current_branch =
79 repo.branch.as_ref().map(|branch| branch.name().to_owned());
80 repo.send_job(None, |state, _| async move {
81 let RepositoryState::Local(LocalRepositoryState { backend, .. }) =
82 state
83 else {
84 return GitState {
85 remote_url: None,
86 head_sha: None,
87 current_branch,
88 diff: None,
89 };
90 };
91
92 let remote_url = backend.remote_url("origin").await;
93 let head_sha = backend.head_sha().await;
94 let diff = backend.diff(DiffType::HeadToWorktree).await.ok();
95
96 GitState {
97 remote_url,
98 head_sha,
99 current_branch,
100 diff,
101 }
102 })
103 })
104 });
105
106 let git_state = match git_state {
107 Some(receiver) => receiver.await.ok(),
108 None => None,
109 };
110
111 TelemetryWorktreeSnapshot {
112 worktree_path,
113 git_state,
114 }
115 })
116 }
117}