Skip to content

feat(core): add more IO extensions (exists?, mkdir, rmdir, cwd)#1518

Closed
sqrew wants to merge 4 commits intocarp-lang:masterfrom
sqrew:stdlib-io-extensions
Closed

feat(core): add more IO extensions (exists?, mkdir, rmdir, cwd)#1518
sqrew wants to merge 4 commits intocarp-lang:masterfrom
sqrew:stdlib-io-extensions

Conversation

@sqrew
Copy link
Copy Markdown
Contributor

@sqrew sqrew commented Mar 25, 2026

Adds several cross-platform IO extensions to the standard library:

  • IO.file-exists? and IO.dir-exists?
  • IO.create-dir (wraps mkdir)
  • IO.remove-dir (wraps rmdir)
  • IO.get-cwd (wraps getcwd)
  • IO.set-cwd (wraps chdir)

Includes corresponding cross-platform C wrappers in core/carp_io.h and comprehensive tests in test/io.carp.

sqrew added 2 commits March 21, 2026 14:28
…set-cwd)

Adds cross-platform wrappers for mkdir, rmdir, getcwd, and chdir to IO.Raw
and high-level Result-returning versions to the IO module. Also fixes several
typos in existing IO documentation and updates tests to cover new functionality.
Comment thread core/carp_io.h
#include <direct.h>
#define getcwd _getcwd
#define chdir _chdir
#else
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like Windows is not happy with this change quite yet.

@sqrew sqrew force-pushed the stdlib-io-extensions branch from 04d80da to c9fba4a Compare April 2, 2026 19:46
@sqrew
Copy link
Copy Markdown
Contributor Author

sqrew commented Apr 2, 2026

Added cross-platform Windows implementation for list-dir and get-cwd, and fixed the missing headers in carp_io.h. Consolidated tests into test/io.carp. Thanks!

@eriksvedang eriksvedang requested a review from hellerve April 9, 2026 07:34
Comment thread core/carp_io.h Outdated
}
closedir(d);
} else {
result.len = -1;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think returning an array of length -1 is safe or sensible here. We should find a different error condition (also for all the functions on top of this one).

Comment thread core/IO.carp
Comment on lines +247 to +252
(doc file-exists? "Checks if a file exists at the given path.")
(register file-exists? (Fn [&String] Bool))

(doc dir-exists? "Checks if a directory exists at the given path.")
(register dir-exists? (Fn [&String] Bool))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those should probably also be in IO.Raw.

Comment thread core/IO.carp Outdated
(let [res (IO.Raw.get-cwd)]
(if (null? res)
(Result.Error (fmt "Failed to get current working directory: %s" &(System.error-text)))
(let [s (String.from-cstr res)]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The C side already returns a String. We should fix the function signature on the C side to avoid a double allocation.

Comment thread core/IO.carp Outdated
""
"Returns a (Result Bool String) indicating success or failure.")
(defn copy-file [src dest]
(Result.and-then (read-file src)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads the complete file into memory. We should probably do a chunked read-write.

Comment thread core/carp_io.h Outdated
#ifdef _WIN32
return _mkdir(*path);
#else
return mkdir(*path, 0777);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is using 0777 really desirable here?

Comment thread core/IO.carp
(doc open-file
"Opens a [FILE](#file) with the given name using a designated mode"
"(e.g. [r]ead, [w]rite, [a]ppend), [rb] read binary...)."
"(e.g. [r]ead, [w]rite, [a]ppend), [rb] read binary...."
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We open a paren here, so we should also close it.

Comment thread test/io.carp
data
&(write-then-read data "io_carp_testdata.txt")
"write-file then read-file" ))
(let [res (write-then-read data "io_carp_testdata.txt")]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the let change here? Should be fine without?

Comment thread test/io.carp
"append-file appends to existing file"))))
(do
(IO.Raw.unlink! file-name)
(let [s (Result.unsafe-from-success result)]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same let-change here. Why? We also don’t need a do inside a let-do.

- Fixed IO.Raw.get-cwd to avoid double allocation.
- Refactored IO.Raw.list-dir to use out-parameter instead of -1 length.
- Improved IO.copy-file with chunked read-write loop.
- Tightened IO.Raw.mkdir permissions to 0755.
- Cleaned up stylistic issues (redundant do/let blocks).
- Fixed typos and syntax.
Comment thread core/IO.carp
""
"Returns a (Result String String) indicating success or failure.")
(defn get-cwd []
(let [res (IO.Raw.get-cwd)]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ths can return NULL. We need to check for that.

Comment thread core/IO.carp
"Returns a (Result Bool String) indicating success or failure.")
(defn copy-file [src dest]
(let [f-src? (open-file src "rb")
f-dest? (open-file dest "wb")]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ths will create and open the file before we validated the source is valid.

Comment thread core/IO.carp
(let-do [f-src (Result.unsafe-from-success f-src?)
f-dest (Result.unsafe-from-success f-dest?)
buf-size 4096
buffer (String.allocate buf-size \0)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we validate the allocation like we do in read-file?

Comment thread core/carp_io.h
HANDLE hFind = FindFirstFile(search_path, &fd);
if (hFind != INVALID_HANDLE_VALUE) {
out_result->len = 0;
out_result->capacity = 0;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to update capacity!

Comment thread core/carp_io.h
}
String name = String_from_MINUS_cstr(dir->d_name);
out_result->len++;
out_result->data = CARP_REALLOC(out_result->data, sizeof(String) * out_result->len);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don’t reallocate on every directory, this is quadratic. Grow geometrically like arrays do.

@sqrew
Copy link
Copy Markdown
Contributor Author

sqrew commented Apr 28, 2026

IO is more complicated than I anticipated so I will close this in favor of someone more qualified attempting it

@sqrew sqrew closed this Apr 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants