I assume this is to open a file?

Personally, I took the approach of splitting into 3 separate functions; openRead, openWrite, openReadWrite.

This allows for different return types which inherit from a single common one, and makes clear the contract for which that thing (interface) can and cannot do. Eliminates nearly all API confusion and/or misuse.

openRead doesn't need to take parameters as it must always exist.

openWrite and openReadWrite take an exclusivity enum MaybeCreate, MustCreate, MustExist. This also allows a mode to be expressed for applying appropriate permissions immediately on creation when MaybyCreate and MustCreate are expressed.

openWrite also takes an append boolean parameter, constraining appending/truncation behavior to that function only.

I spent a lot of time playing with my API, bumping into and figuring out workarounds for several platform specific limitations (such as pwrite with O_APPEND being broken on Linux/FreeBSD).

https://github.com/05nelsonm/kmp-file/blob/master/library/file/src/commonMain/kotlin/io/matthewnelson/kmp/file/OpenExcl.kt

https://github.com/05nelsonm/kmp-file/blob/master/library/file/src/commonMain/kotlin/io/matthewnelson/kmp/file/FileStream.kt

Reply to this note

Please Login to reply.

Discussion

I love this pattern: excellent advice. I asked ChatGPT how to do this in Rust, and it suggested a template using a phantom type was idiomatic Rust:

pub struct ReadOnly;

pub struct Writable;

pub struct Store {

file: File,

_mode: PhantomData,

}

Then implement write() only in the writable specialization.

I've deferred this change for now, but congratulations: you are responsible for my first refactoring (once the implementation is complete!) 🧡