1use crate::core::{Artifact, ArtifactKind, ArtifactMetadata, MarkerKind, ProjectKind, ProjectMarker};
4use crate::error::Result;
5use crate::plugins::Plugin;
6use std::path::Path;
7
8pub struct DotNetPlugin;
10
11impl Plugin for DotNetPlugin {
12 fn id(&self) -> &'static str {
13 "dotnet"
14 }
15
16 fn name(&self) -> &'static str {
17 ".NET"
18 }
19
20 fn supported_kinds(&self) -> &[ProjectKind] {
21 &[ProjectKind::DotNet, ProjectKind::FSharp]
22 }
23
24 fn markers(&self) -> Vec<ProjectMarker> {
25 vec![
26 ProjectMarker {
27 indicator: MarkerKind::AnyOf(vec!["*.csproj", "*.fsproj", "*.vbproj"]),
28 kind: ProjectKind::DotNet,
29 priority: 55,
30 },
31 ProjectMarker {
32 indicator: MarkerKind::File("*.sln"),
33 kind: ProjectKind::DotNet,
34 priority: 50,
35 },
36 ]
37 }
38
39 fn detect(&self, path: &Path) -> Option<ProjectKind> {
40 if let Ok(entries) = std::fs::read_dir(path) {
42 for entry in entries.filter_map(|e| e.ok()) {
43 if let Some(name) = entry.file_name().to_str() {
44 if name.ends_with(".csproj")
45 || name.ends_with(".vbproj")
46 || name.ends_with(".sln")
47 {
48 return Some(ProjectKind::DotNet);
49 }
50 if name.ends_with(".fsproj") {
51 return Some(ProjectKind::FSharp);
52 }
53 }
54 }
55 }
56 None
57 }
58
59 fn find_artifacts(&self, project_root: &Path) -> Result<Vec<Artifact>> {
60 let mut artifacts = Vec::new();
61
62 let bin = project_root.join("bin");
64 if bin.exists() {
65 artifacts.push(Artifact {
66 path: bin,
67 kind: ArtifactKind::BuildOutput,
68 size: 0,
69 file_count: 0,
70 age: None,
71 metadata: ArtifactMetadata::restorable("dotnet build"),
72 });
73 }
74
75 let obj = project_root.join("obj");
77 if obj.exists() {
78 artifacts.push(Artifact {
79 path: obj,
80 kind: ArtifactKind::BuildOutput,
81 size: 0,
82 file_count: 0,
83 age: None,
84 metadata: ArtifactMetadata::restorable("dotnet build"),
85 });
86 }
87
88 let packages = project_root.join("packages");
90 if packages.exists() {
91 artifacts.push(Artifact {
92 path: packages,
93 kind: ArtifactKind::Dependencies,
94 size: 0,
95 file_count: 0,
96 age: None,
97 metadata: ArtifactMetadata::restorable("dotnet restore"),
98 });
99 }
100
101 let test_results = project_root.join("TestResults");
103 if test_results.exists() {
104 artifacts.push(Artifact {
105 path: test_results,
106 kind: ArtifactKind::TestOutput,
107 size: 0,
108 file_count: 0,
109 age: None,
110 metadata: ArtifactMetadata::restorable("dotnet test"),
111 });
112 }
113
114 Ok(artifacts)
115 }
116
117 fn cleanable_dirs(&self) -> &[&'static str] {
118 &["bin", "obj", "packages", "TestResults"]
119 }
120
121 fn priority(&self) -> u8 {
122 55
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use tempfile::TempDir;
130
131 #[test]
132 fn test_detect_dotnet() {
133 let temp = TempDir::new().unwrap();
134 std::fs::write(temp.path().join("MyApp.csproj"), "<Project></Project>").unwrap();
135
136 let plugin = DotNetPlugin;
137 assert_eq!(plugin.detect(temp.path()), Some(ProjectKind::DotNet));
138 }
139
140 #[test]
141 fn test_detect_fsharp() {
142 let temp = TempDir::new().unwrap();
143 std::fs::write(temp.path().join("MyApp.fsproj"), "<Project></Project>").unwrap();
144
145 let plugin = DotNetPlugin;
146 assert_eq!(plugin.detect(temp.path()), Some(ProjectKind::FSharp));
147 }
148
149 #[test]
150 fn test_find_artifacts() {
151 let temp = TempDir::new().unwrap();
152 std::fs::write(temp.path().join("App.csproj"), "").unwrap();
153 std::fs::create_dir(temp.path().join("bin")).unwrap();
154 std::fs::create_dir(temp.path().join("obj")).unwrap();
155
156 let plugin = DotNetPlugin;
157 let artifacts = plugin.find_artifacts(temp.path()).unwrap();
158
159 assert_eq!(artifacts.len(), 2);
160 }
161}