sqry_daemon/workspace/
builder.rs1use std::{path::Path, sync::Arc};
16
17use sqry_core::graph::CodeGraph;
18
19use crate::error::DaemonError;
20
21pub trait WorkspaceBuilder: Send + Sync + std::fmt::Debug {
30 fn build(&self, workspace_root: &Path) -> Result<CodeGraph, DaemonError>;
40}
41
42impl<T: WorkspaceBuilder + ?Sized> WorkspaceBuilder for Arc<T> {
45 fn build(&self, workspace_root: &Path) -> Result<CodeGraph, DaemonError> {
46 (**self).build(workspace_root)
47 }
48}
49
50#[doc(hidden)]
54#[derive(Debug, Default, Clone, Copy)]
55pub struct EmptyGraphBuilder;
56
57impl WorkspaceBuilder for EmptyGraphBuilder {
58 fn build(&self, _workspace_root: &Path) -> Result<CodeGraph, DaemonError> {
59 Ok(CodeGraph::new())
60 }
61}
62
63#[doc(hidden)]
67#[derive(Debug, Clone)]
68pub struct FailingGraphBuilder {
69 pub reason: String,
71}
72
73impl FailingGraphBuilder {
74 #[must_use]
76 pub fn new(reason: impl Into<String>) -> Self {
77 Self {
78 reason: reason.into(),
79 }
80 }
81}
82
83impl WorkspaceBuilder for FailingGraphBuilder {
84 fn build(&self, workspace_root: &Path) -> Result<CodeGraph, DaemonError> {
85 Err(DaemonError::WorkspaceBuildFailed {
86 root: workspace_root.to_path_buf(),
87 reason: self.reason.clone(),
88 })
89 }
90}
91
92pub struct RealWorkspaceBuilder {
100 plugins: Arc<sqry_core::plugin::PluginManager>,
101 build_config: sqry_core::graph::unified::build::BuildConfig,
102}
103
104impl std::fmt::Debug for RealWorkspaceBuilder {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 f.debug_struct("RealWorkspaceBuilder")
109 .field(
110 "plugins",
111 &format_args!("<PluginManager@{:p}>", Arc::as_ptr(&self.plugins)),
112 )
113 .field("build_config", &self.build_config)
114 .finish()
115 }
116}
117
118impl RealWorkspaceBuilder {
119 #[must_use]
122 pub fn new(plugins: Arc<sqry_core::plugin::PluginManager>) -> Self {
123 Self {
124 plugins,
125 build_config: sqry_core::graph::unified::build::BuildConfig::default(),
126 }
127 }
128
129 #[must_use]
132 pub fn with_build_config(
133 plugins: Arc<sqry_core::plugin::PluginManager>,
134 build_config: sqry_core::graph::unified::build::BuildConfig,
135 ) -> Self {
136 Self {
137 plugins,
138 build_config,
139 }
140 }
141}
142
143impl WorkspaceBuilder for RealWorkspaceBuilder {
144 fn build(&self, workspace_root: &Path) -> Result<CodeGraph, DaemonError> {
145 sqry_core::graph::unified::build::build_unified_graph(
146 workspace_root,
147 &self.plugins,
148 &self.build_config,
149 )
150 .map_err(|e| DaemonError::WorkspaceBuildFailed {
151 root: workspace_root.to_path_buf(),
152 reason: e.to_string(),
153 })
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn empty_builder_returns_fresh_graph() {
163 let b = EmptyGraphBuilder;
164 let g = b.build(Path::new("/repos/example")).expect("always ok");
165 assert_eq!(g.node_count(), 0);
166 }
167
168 #[test]
169 fn failing_builder_surfaces_reason_and_root() {
170 let b = FailingGraphBuilder::new("plugin panic");
171 let err = b
172 .build(Path::new("/repos/example"))
173 .expect_err("always fails");
174 match err {
175 DaemonError::WorkspaceBuildFailed { root, reason } => {
176 assert_eq!(root, Path::new("/repos/example"));
177 assert_eq!(reason, "plugin panic");
178 }
179 other => panic!("wrong variant: {other:?}"),
180 }
181 }
182
183 #[test]
184 fn arc_builder_passes_through_to_inner() {
185 let inner: Arc<dyn WorkspaceBuilder> = Arc::new(EmptyGraphBuilder);
186 let g = inner
187 .build(Path::new("/repos/example"))
188 .expect("arc-wrapped builder delegates");
189 assert_eq!(g.node_count(), 0);
190 }
191}