Skip to content

Commit 18b22ae

Browse files
📝 Add docstrings to feat/carbon-dev
Docstrings generation was requested by @CoderSerio. * #2 (comment) The following files were modified: * `benchmark/bench.ts` * `benchmark/rm.ts` * `src/readdir.rs` * `src/rm.rs`
1 parent c8bf7bc commit 18b22ae

File tree

4 files changed

+165
-4
lines changed

4 files changed

+165
-4
lines changed

benchmark/bench.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import { fileURLToPath } from 'node:url'
44

55
const __dirname = path.dirname(fileURLToPath(import.meta.url))
66

7+
/**
8+
* Discovers and runs benchmark files in the current directory, optionally filtered by the first command-line argument.
9+
*
10+
* Selects files ending with `.ts` (excluding `bench.ts` and declaration files ending with `.d.ts`). If a filter string is provided as the first CLI argument, only files whose names include that substring (case-insensitive) are selected. Logs a message and exits if no files match, logs the number of files found, and imports each matched file to execute its benchmark; errors importing individual files are logged.
11+
*/
712
async function runBenchmarks() {
813
const args = process.argv.slice(2)
914
const filter = args[0]
@@ -38,4 +43,4 @@ async function runBenchmarks() {
3843
}
3944
}
4045

41-
runBenchmarks()
46+
runBenchmarks()

benchmark/rm.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,31 @@ if (fs.existsSync(baseDir)) {
1212
}
1313
fs.mkdirSync(baseDir)
1414

15+
/**
16+
* Create a flat directory containing a specified number of files.
17+
*
18+
* Ensures the target directory exists and writes `count` files named
19+
* `file-0.txt` through `file-(count-1).txt`, each containing the string "content".
20+
*
21+
* @param dir - The directory path where files will be created
22+
* @param count - The number of files to create (non-negative integer)
23+
*/
1524
function createFlatStructure(dir: string, count: number) {
1625
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
1726
for (let i = 0; i < count; i++) {
1827
fs.writeFileSync(path.join(dir, `file-${i}.txt`), 'content')
1928
}
2029
}
2130

31+
/**
32+
* Creates a chain of nested subdirectories under the given root and places a file in each level.
33+
*
34+
* Ensures the root directory exists, then creates `depth` nested directories named `depth-0`, `depth-1`, ...,
35+
* and writes a file named `file.txt` containing "content" into each created subdirectory.
36+
*
37+
* @param dir - The root directory under which the nested structure will be created
38+
* @param depth - The number of nested subdirectory levels to create
39+
*/
2240
function createDeepStructure(dir: string, depth: number) {
2341
let current = dir
2442
if (!fs.existsSync(current)) fs.mkdirSync(current, { recursive: true })
@@ -29,6 +47,15 @@ function createDeepStructure(dir: string, depth: number) {
2947
}
3048
}
3149

50+
/**
51+
* Runs a benchmark group that compares multiple rmSync implementations and prints a Mitata-like comparison table.
52+
*
53+
* Executes a warmup run then performs 10 timed iterations per implementation using the provided setup function to create
54+
* each test directory (setup time is excluded from measurements). For each implementation it computes the average time
55+
* in milliseconds and prints each implementation's average alongside a ratio compared to the first (baseline).
56+
*
57+
* @param setupFn - A function that creates the test directory structure at the given path before removal
58+
*/
3259
async function runGroup(groupName: string, setupFn: (dir: string) => void) {
3360
console.log(`\n${groupName}`)
3461

@@ -84,6 +111,11 @@ async function runGroup(groupName: string, setupFn: (dir: string) => void) {
84111
})
85112
}
86113

114+
/**
115+
* Execute the benchmark suite for the two test scenarios and remove temporary data.
116+
*
117+
* Runs the "Flat directory (2000 files)" and "Deep nested directory (depth 100)" benchmark groups sequentially, then deletes the temporary base directory if it exists.
118+
*/
87119
async function run() {
88120
await runGroup('Flat directory (2000 files)', (dir) => createFlatStructure(dir, 2000))
89121
await runGroup('Deep nested directory (depth 100)', (dir) => createDeepStructure(dir, 100))
@@ -94,4 +126,4 @@ async function run() {
94126
}
95127
}
96128

97-
run()
129+
run()

src/readdir.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,31 @@ pub struct Dirent {
3939
}
4040

4141
// #[napi] // marco: expose the function to Node
42+
/// Read directory entries from `path_str` according to the provided `options`.
43+
///
44+
/// The function performs a non-recursive or recursive directory listing based on `options.recursive`.
45+
/// Hidden entries may be skipped when `options.skip_hidden` is true. When `options.with_file_types`
46+
/// is true the results include `Dirent` objects with `name`, `parent_path`, and `is_dir`; otherwise
47+
/// the results are plain file path strings (non-recursive: entry names; recursive: paths relative
48+
/// to the provided root).
49+
///
50+
/// # Returns
51+
///
52+
/// `Ok(Either::A(Vec<String>))` with entry names or relative paths when `with_file_types` is false,
53+
/// or `Ok(Either::B(Vec<Dirent>))` with `Dirent` objects when `with_file_types` is true. Returns
54+
/// `Err` when the path does not exist or an underlying IO error occurs (error message contains
55+
/// the underlying reason).
56+
///
57+
/// # Examples
58+
///
59+
/// ```
60+
/// // Non-recursive list of names
61+
/// let res = ls(".".to_string(), None).unwrap();
62+
/// match res {
63+
/// Either::A(names) => println!("names: {:?}", names),
64+
/// Either::B(dirents) => println!("dirents: {:?}", dirents),
65+
/// }
66+
/// ```
4267
fn ls(
4368
path_str: String,
4469
options: Option<ReaddirOptions>,
@@ -184,4 +209,4 @@ impl Task for ReaddirTask {
184209
#[napi(js_name = "readdir")]
185210
pub fn readdir(path: String, options: Option<ReaddirOptions>) -> AsyncTask<ReaddirTask> {
186211
AsyncTask::new(ReaddirTask { path, options })
187-
}
212+
}

src/rm.rs

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,43 @@ pub struct RmOptions {
3030
pub concurrency: Option<u32>,
3131
}
3232

33+
/// Recursively removes the file or directory at `path` according to `opts`.
34+
///
35+
/// If `path` is a directory and `opts.recursive` is `true`, the directory's
36+
/// contents are removed first (optionally in parallel when `opts.concurrency`
37+
/// is greater than 1) and then the directory itself is removed. If the path
38+
/// is a directory and `opts.recursive` is `false`, the function attempts to
39+
/// remove the directory and maps "directory not empty" conditions to an
40+
/// `ENOTEMPTY`-style error message. If the path is not a directory, the file
41+
/// is removed.
42+
///
43+
/// # Parameters
44+
///
45+
/// - `path`: filesystem path to remove.
46+
/// - `opts`: removal options; `recursive` controls directory recursion and
47+
/// `concurrency` (when > 1) enables parallel traversal.
48+
///
49+
/// # Returns
50+
///
51+
/// `Ok(())` on successful removal, or an `napi::Error` created from the
52+
/// underlying I/O error on failure.
53+
///
54+
/// # Examples
55+
///
56+
/// ```no_run
57+
/// use std::path::Path;
58+
///
59+
/// let opts = RmOptions {
60+
/// force: None,
61+
/// max_retries: None,
62+
/// recursive: Some(true),
63+
/// retry_delay: None,
64+
/// concurrency: None,
65+
/// };
66+
///
67+
/// // Remove the current directory contents (for demonstration; be careful)
68+
/// let _ = remove_recursive(Path::new("."), &opts).unwrap();
69+
/// ```
3370
fn remove_recursive(path: &Path, opts: &RmOptions) -> Result<()> {
3471
let meta = fs::symlink_metadata(path).map_err(|e| Error::from_reason(e.to_string()))?;
3572

@@ -72,6 +109,31 @@ fn remove_recursive(path: &Path, opts: &RmOptions) -> Result<()> {
72109
Ok(())
73110
}
74111

112+
/// Remove the filesystem entry at `path_str` using the provided rm-style options.
113+
///
114+
/// The empty string for `path_str` is treated as `"."`. When `options` is `None`,
115+
/// defaults are used (force = false, recursive = false, other fields unset).
116+
/// If `options.force` is true and the path does not exist, the call succeeds silently.
117+
///
118+
/// # Parameters
119+
///
120+
/// - `path_str` — Path to remove; `""` is interpreted as the current directory (`"."`).
121+
/// - `options` — Optional `RmOptions` controlling behavior (e.g. `force`, `recursive`, `concurrency`).
122+
///
123+
/// # Returns
124+
///
125+
/// `Ok(())` on successful removal, or an `Err` containing a `napi::Error` describing the failure.
126+
///
127+
/// # Examples
128+
///
129+
/// ```
130+
/// // remove a single file (non-recursive)
131+
/// let _ = remove("tmp/file.txt".to_string(), None);
132+
///
133+
/// // remove or ignore missing path
134+
/// let opts = RmOptions { force: Some(true), recursive: Some(false), max_retries: None, retry_delay: None, concurrency: None };
135+
/// let _ = remove("tmp/missing".to_string(), Some(opts));
136+
/// ```
75137
fn remove(path_str: String, options: Option<RmOptions>) -> Result<()> {
76138
let search_path_str = if path_str.is_empty() { "." } else { &path_str };
77139
let path = Path::new(search_path_str);
@@ -110,21 +172,58 @@ impl Task for RmTask {
110172
type Output = ();
111173
type JsValue = ();
112174

175+
/// Performs the removal operation described by this task.
176+
///
177+
/// # Examples
178+
///
179+
/// ```no_run
180+
/// let mut task = RmTask { path: ".".to_string(), options: None };
181+
/// let result = task.compute();
182+
/// assert!(result.is_ok());
183+
/// ```
113184
fn compute(&mut self) -> Result<Self::Output> {
114185
remove(self.path.clone(), self.options.clone())
115186
}
116187

188+
/// Convert the completed task result into a JavaScript value for the N-API environment.
189+
///
190+
/// This implementation produces no JavaScript value and signals successful resolution.
191+
///
192+
/// # Returns
193+
///
194+
/// `Ok(())` indicating the task resolved with no value to return to JavaScript.
117195
fn resolve(&mut self, _env: Env, _output: Self::Output) -> Result<Self::JsValue> {
118196
Ok(())
119197
}
120198
}
121199

200+
/// Creates an asynchronous remove task for the given filesystem path using the provided options.
201+
///
202+
/// The returned task, when scheduled by the N-API runtime, will perform the removal work off the
203+
/// main thread and resolve with no value.
204+
///
205+
/// # Examples
206+
///
207+
/// ```
208+
/// let task = rm("some/path".to_string(), None);
209+
/// // `task` can be returned to JavaScript or scheduled with the napi runtime.
210+
/// ```
122211
#[napi(js_name = "rm")]
123212
pub fn rm(path: String, options: Option<RmOptions>) -> AsyncTask<RmTask> {
124213
AsyncTask::new(RmTask { path, options })
125214
}
126215

216+
/// Synchronously removes the filesystem entry at the given path using the provided options.
217+
///
218+
/// Returns `Ok(())` on success or an error describing the failure.
219+
///
220+
/// # Examples
221+
///
222+
/// ```
223+
/// // Remove a file or directory at "./tmp" using default options.
224+
/// rm_sync("./tmp".to_string(), None).unwrap();
225+
/// ```
127226
#[napi(js_name = "rmSync")]
128227
pub fn rm_sync(path: String, options: Option<RmOptions>) -> Result<()> {
129228
remove(path, options)
130-
}
229+
}

0 commit comments

Comments
 (0)