dotnet_lens/lib.rs
1//! dotnet-lens is a library for listing dependencies between .NET projects and packages.
2//!
3//! This library provides functionality to parse .NET project files (`.csproj`, `.fsproj`, `.vbproj`)
4//! and extract information about project dependencies, including project references and package references.
5//!
6//! ## Overview
7//!
8//! The main components of this library include:
9//! - `Project`: A struct representing a .NET project, including its language, name, path, target framework,
10//! project references, and package references.
11//! - `ProjectLanguage`: An enum representing the language of the project based on the file extension.
12//! - `ProjectReference`: A struct representing a reference to another project.
13//! - `PackageReference`: A struct representing a reference to a NuGet package.
14//!
15//! ## Modules
16//!
17//! - `parser`: A module for parsing .NET project files and extracting dependency information.
18//! - `search`: A module for searching .NET project files in a directory.
19//!
20//! ## Features
21//! - `serde`: Adds support for serde serialization and deserialization for the Project struct and
22//! adjacent types
23//!
24//! ## Examples
25//!
26//! Here is a brief example demonstrating how to use the `Project` struct and its methods:
27//!
28//! ```no_run
29//! use dotnet_lens::{Project, search};
30//!
31//! let projects_paths = search::search_projects(&"path/to/repository")?;
32//!
33//! for path in projects_paths {
34//! let project = Project::new(path)?;
35//!
36//! for package_reference in project.package_references() {
37//! println!("{}: {}", package_reference.name(), package_reference.version());
38//! }
39//! }
40//!
41//! # Ok::<(), Box<dyn std::error::Error>>(())
42//! ```
43
44use std::{
45 ffi::OsStr,
46 fs::File,
47 path::{Path, PathBuf},
48};
49
50use parser::ParseError;
51
52pub mod parser;
53pub mod search;
54
55/// List of valid extensions: "csproj", "fsproj", "vbproj".
56pub const VALID_EXTENSIONS: [&str; 3] = ["csproj", "fsproj", "vbproj"];
57
58/// Represents a .NET project.
59#[derive(Debug, Clone, PartialEq)]
60#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
61pub struct Project {
62 name: String,
63 language: ProjectLanguage,
64 path: PathBuf,
65 target_framework: Option<String>,
66 project_references: Vec<ProjectReference>,
67 package_references: Vec<PackageReference>,
68}
69
70impl Project {
71 /// Creates a new `Project` instance by parsing a .NET project file.
72 ///
73 /// This function opens the file specified by the `path`, reads its contents, and parses it
74 /// into a `Project` instance using the `parser::parse` function.
75 ///
76 /// # Arguments
77 ///
78 /// * `path` - A path to the .NET project file that implements the `AsRef<Path>` trait.
79 ///
80 /// # Returns
81 ///
82 /// A `Result<Self, ParseError>` where `Ok(Self)` contains the parsed `Project` instance, and
83 /// `Err(ParseError)` contains the error if the file could not be read or parsed.
84 ///
85 /// # Errors
86 ///
87 /// This function will return an error if:
88 /// - The file could not be opened.
89 /// - The file could not be parsed as a .NET project file.
90 ///
91 /// # Examples
92 ///
93 /// ```rust
94 /// use dotnet_lens::{Project, parser::ParseError};
95 /// use std::path::Path;
96 ///
97 /// match Project::new(Path::new("path/to/MyProject.csproj")) {
98 /// Ok(project) => {
99 /// println!("Project parsed successfully: {:?}", project);
100 /// },
101 /// Err(e) => {
102 /// eprintln!("Failed to parse project: {:?}", e);
103 /// }
104 /// }
105 /// ```
106 pub fn new<P>(path: P) -> Result<Self, ParseError>
107 where
108 P: AsRef<Path>,
109 {
110 let file_reader = File::open(path.as_ref())?;
111
112 parser::parse(file_reader, path)
113 }
114
115 /// Returns the name of the project based on the file name of the provided path.
116 ///
117 /// # Arguments
118 ///
119 /// * `path` - A reference to a path that implements the `AsRef<Path>` trait.
120 ///
121 /// # Returns
122 ///
123 /// An `Option<String>` containing the project name if it could be extracted, otherwise `None`.
124 pub fn get_project_name<P>(path: P) -> Option<String>
125 where
126 P: AsRef<Path>,
127 {
128 path.as_ref()
129 .file_stem()
130 .and_then(|name| name.to_str())
131 .map(|name| name.to_string())
132 }
133
134 /// Returns the name of the project.
135 pub fn name(&self) -> &String {
136 &self.name
137 }
138
139 /// Returns the language of the project.
140 pub fn language(&self) -> ProjectLanguage {
141 self.language
142 }
143
144 /// Returns the path of the project.
145 pub fn path(&self) -> &PathBuf {
146 &self.path
147 }
148
149 /// Returns the target framework of the project, if any.
150 pub fn target_framework(&self) -> Option<&String> {
151 self.target_framework.as_ref()
152 }
153
154 /// Returns a reference to the list of project references.
155 pub fn project_references(&self) -> &Vec<ProjectReference> {
156 &self.project_references
157 }
158
159 /// Adds a new project reference to the list of project references.
160 pub fn add_project_reference(&mut self, value: ProjectReference) {
161 self.project_references.push(value);
162 }
163
164 /// Returns a reference to the list of package references.
165 pub fn package_references(&self) -> &Vec<PackageReference> {
166 &self.package_references
167 }
168
169 /// Adds a new package reference to the list of package references.
170 pub fn add_package_reference(&mut self, value: PackageReference) {
171 self.package_references.push(value);
172 }
173}
174
175/// Represents the language of a .NET project based on the file extension.
176#[derive(Debug, Copy, Clone, PartialEq)]
177#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
178pub enum ProjectLanguage {
179 CSharp,
180 FSharp,
181 VB,
182}
183
184impl ProjectLanguage {
185 /// Determines the project language from the file extension.
186 ///
187 /// The extension must be provided without a "." (ex: "csproj", "fsproj", "vbproj").
188 ///
189 /// # Arguments
190 ///
191 /// * `extension` - A reference to an `OsStr` representing the file extension.
192 ///
193 /// # Returns
194 ///
195 /// An `Option<ProjectLanguage>` containing the project language if it could be determined, otherwise `None`.
196 pub fn from_extension(extension: &OsStr) -> Option<Self> {
197 match extension.to_str() {
198 Some(val) => match val {
199 "csproj" => Some(Self::CSharp),
200 "fsproj" => Some(Self::FSharp),
201 "vbproj" => Some(Self::VB),
202 _ => None,
203 }
204 _ => None,
205 }
206 }
207}
208
209/// Represents a reference to another .NET project.
210#[derive(Debug, Clone, PartialEq, Default)]
211#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
212pub struct ProjectReference {
213 name: String,
214 path: PathBuf,
215}
216
217impl ProjectReference {
218 /// Creates a new `ProjectReference` instance with the specified name and path.
219 ///
220 /// # Arguments
221 ///
222 /// * `name` - The name of the referenced project.
223 /// * `path` - The path to the referenced project file.
224 ///
225 /// # Examples
226 ///
227 /// ```rust
228 /// use dotnet_lens::ProjectReference;
229 /// use std::path::PathBuf;
230 ///
231 /// let project_ref = ProjectReference::new("OtherProject".to_string(), PathBuf::from("path/to/OtherProject.csproj"));
232 /// println!("Project Name: {}", project_ref.name());
233 /// println!("Project Path: {:?}", project_ref.path());
234 /// ```
235 pub fn new(name: String, path: PathBuf) -> Self {
236 Self { name, path }
237 }
238
239 /// Returns the name of the referenced project.
240 ///
241 /// # Examples
242 ///
243 /// ```rust
244 /// use dotnet_lens::ProjectReference;
245 /// use std::path::PathBuf;
246 ///
247 /// let project_ref = ProjectReference::new("OtherProject".to_string(), PathBuf::from("path/to/OtherProject.csproj"));
248 /// println!("Project Name: {}", project_ref.name());
249 /// ```
250 pub fn name(&self) -> &String {
251 &self.name
252 }
253
254 /// Returns the path to the referenced project file.
255 ///
256 /// # Examples
257 ///
258 /// ```rust
259 /// use dotnet_lens::ProjectReference;
260 /// use std::path::PathBuf;
261 ///
262 /// let project_ref = ProjectReference::new("OtherProject".to_string(), PathBuf::from("path/to/OtherProject.csproj"));
263 /// println!("Project Path: {:?}", project_ref.path());
264 /// ```
265 pub fn path(&self) -> &Path {
266 &self.path
267 }
268}
269
270/// Represents a reference to a NuGet package.
271#[derive(Debug, Clone, PartialEq, Default)]
272#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
273pub struct PackageReference {
274 name: String,
275 version: String,
276}
277
278impl PackageReference {
279 /// Creates a new `PackageReference` instance with the specified name and version.
280 ///
281 /// # Arguments
282 ///
283 /// * `name` - The name of the package.
284 /// * `version` - The version of the package.
285 ///
286 /// # Examples
287 ///
288 /// ```rust
289 /// use dotnet_lens::PackageReference;
290 ///
291 /// let package_ref = PackageReference::new("MyPackage".to_string(), "1.0.0".to_string());
292 /// println!("Package Name: {}", package_ref.name());
293 /// println!("Package Version: {}", package_ref.version());
294 /// ```
295 pub fn new(name: String, version: String) -> Self {
296 Self { name, version }
297 }
298
299 /// Returns the name of the package.
300 ///
301 /// # Examples
302 ///
303 /// ```rust
304 /// use dotnet_lens::PackageReference;
305 ///
306 /// let package_ref = PackageReference::new("MyPackage".to_string(), "1.0.0".to_string());
307 /// println!("Package Name: {}", package_ref.name());
308 /// ```
309 pub fn name(&self) -> &String {
310 &self.name
311 }
312
313 /// Returns the version of the package.
314 ///
315 /// # Examples
316 ///
317 /// ```rust
318 /// use dotnet_lens::PackageReference;
319 ///
320 /// let package_ref = PackageReference::new("MyPackage".to_string(), "1.0.0".to_string());
321 /// println!("Package Version: {}", package_ref.version());
322 /// ```
323 pub fn version(&self) -> &String {
324 &self.version
325 }
326}