wrkflw_utils/lib.rs
1// utils crate
2
3use std::path::Path;
4
5pub fn is_workflow_file(path: &Path) -> bool {
6 // First, check for GitLab CI files by name
7 if let Some(file_name) = path.file_name() {
8 let file_name_str = file_name.to_string_lossy().to_lowercase();
9 if file_name_str == ".gitlab-ci.yml" || file_name_str.ends_with("gitlab-ci.yml") {
10 return true;
11 }
12 }
13
14 // Then check for GitHub Actions workflows
15 if let Some(ext) = path.extension() {
16 if ext == "yml" || ext == "yaml" {
17 // Check if the file is in a .github/workflows directory
18 if let Some(parent) = path.parent() {
19 return parent.ends_with(".github/workflows") || parent.ends_with("workflows");
20 } else {
21 // Check if filename contains workflow indicators
22 let filename = path
23 .file_name()
24 .map(|f| f.to_string_lossy().to_lowercase())
25 .unwrap_or_default();
26
27 return filename.contains("workflow")
28 || filename.contains("action")
29 || filename.contains("ci")
30 || filename.contains("cd");
31 }
32 }
33 }
34 false
35}
36
37/// Module for safely handling file descriptor redirection
38///
39/// On Unix systems (Linux, macOS), this module provides true file descriptor
40/// redirection by duplicating stderr and redirecting it to /dev/null.
41///
42/// On Windows systems, the redirection functionality is limited due to platform
43/// differences in file descriptor handling. The functions will execute without
44/// error but stderr may not be fully suppressed.
45pub mod fd {
46 use std::io::Result;
47
48 /// Represents a redirected stderr that can be restored
49 pub struct RedirectedStderr {
50 #[cfg(unix)]
51 original_fd: Option<std::os::unix::io::RawFd>,
52 #[cfg(unix)]
53 null_fd: Option<std::os::unix::io::RawFd>,
54 #[cfg(windows)]
55 _phantom: std::marker::PhantomData<()>,
56 }
57
58 #[cfg(unix)]
59 mod unix_impl {
60 use super::*;
61 use nix::fcntl::{open, OFlag};
62 use nix::sys::stat::Mode;
63 use nix::unistd::{close, dup, dup2};
64 use std::io;
65 use std::os::unix::io::RawFd;
66 use std::path::Path;
67
68 /// Standard file descriptors
69 const STDERR_FILENO: RawFd = 2;
70
71 impl RedirectedStderr {
72 /// Creates a new RedirectedStderr that redirects stderr to /dev/null
73 pub fn to_null() -> Result<Self> {
74 // Duplicate the current stderr fd
75 let stderr_backup = match dup(STDERR_FILENO) {
76 Ok(fd) => fd,
77 Err(e) => return Err(io::Error::other(e)),
78 };
79
80 // Open /dev/null
81 let null_fd = match open(Path::new("/dev/null"), OFlag::O_WRONLY, Mode::empty()) {
82 Ok(fd) => fd,
83 Err(e) => {
84 let _ = close(stderr_backup); // Clean up on error
85 return Err(io::Error::other(e));
86 }
87 };
88
89 // Redirect stderr to /dev/null
90 if let Err(e) = dup2(null_fd, STDERR_FILENO) {
91 let _ = close(stderr_backup); // Clean up on error
92 let _ = close(null_fd);
93 return Err(io::Error::other(e));
94 }
95
96 Ok(RedirectedStderr {
97 original_fd: Some(stderr_backup),
98 null_fd: Some(null_fd),
99 })
100 }
101 }
102
103 impl Drop for RedirectedStderr {
104 /// Automatically restores stderr when the RedirectedStderr is dropped
105 fn drop(&mut self) {
106 if let Some(orig_fd) = self.original_fd.take() {
107 // Restore the original stderr
108 let _ = dup2(orig_fd, STDERR_FILENO);
109 let _ = close(orig_fd);
110 }
111
112 // Close the null fd
113 if let Some(null_fd) = self.null_fd.take() {
114 let _ = close(null_fd);
115 }
116 }
117 }
118 }
119
120 #[cfg(windows)]
121 mod windows_impl {
122 use super::*;
123
124 impl RedirectedStderr {
125 /// Creates a new RedirectedStderr that redirects stderr to NUL on Windows
126 pub fn to_null() -> Result<Self> {
127 // On Windows, we can't easily redirect stderr at the file descriptor level
128 // like we can on Unix systems. This is a simplified implementation that
129 // doesn't actually redirect but provides the same interface.
130 // The actual stderr suppression will need to be handled differently on Windows.
131 Ok(RedirectedStderr {
132 _phantom: std::marker::PhantomData,
133 })
134 }
135 }
136
137 impl Drop for RedirectedStderr {
138 /// No-op drop implementation for Windows
139 fn drop(&mut self) {
140 // Nothing to restore on Windows in this simplified implementation
141 }
142 }
143 }
144
145 /// Run a function with stderr redirected to /dev/null (Unix) or suppressed (Windows), then restore stderr
146 ///
147 /// # Platform Support
148 /// - **Unix (Linux, macOS)**: Fully supported - stderr is redirected to /dev/null
149 /// - **Windows**: Limited support - function executes but stderr may be visible
150 ///
151 /// # Example
152 /// ```
153 /// use wrkflw_utils::fd::with_stderr_to_null;
154 ///
155 /// let result = with_stderr_to_null(|| {
156 /// eprintln!("This will be hidden on Unix");
157 /// 42
158 /// }).unwrap();
159 /// assert_eq!(result, 42);
160 /// ```
161 pub fn with_stderr_to_null<F, T>(f: F) -> Result<T>
162 where
163 F: FnOnce() -> T,
164 {
165 #[cfg(unix)]
166 {
167 let _redirected = RedirectedStderr::to_null()?;
168 Ok(f())
169 }
170 #[cfg(windows)]
171 {
172 // On Windows, we can't easily redirect stderr at the FD level,
173 // so we just run the function without redirection.
174 // This means stderr won't be suppressed on Windows, but the function will work.
175 Ok(f())
176 }
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn test_fd_redirection() {
186 // This test will write to stderr, which should be redirected on Unix
187 // On Windows, it will just run normally without redirection
188 let result = fd::with_stderr_to_null(|| {
189 // This would normally appear in stderr (suppressed on Unix, visible on Windows)
190 eprintln!("This should be redirected to /dev/null on Unix");
191 // Return a test value to verify the function passes through the result
192 42
193 });
194
195 // The function should succeed and return our test value on both platforms
196 assert!(result.is_ok());
197 assert_eq!(result.unwrap(), 42);
198 }
199}