///|
fn null_terminated_path(path : String) -> Bytes {
  let encoded = @utf8.encode(path)
  Bytes::makei(encoded.length() + 1, index => {
    if index < encoded.length() {
      encoded[index]
    } else {
      b'\x00'
    }
  })
}

///|
#borrow(temporary, target)
extern "C" fn atomic_replace_ffi(temporary : Bytes, target : Bytes) -> Int = "moonsearch_atomic_replace"

///|
#borrow(path)
extern "C" fn sync_file_ffi(path : Bytes) -> Int = "moonsearch_sync_file"

///|
#borrow(path)
extern "C" fn try_lock_ffi(path : Bytes) -> Int64 = "moonsearch_try_lock"

///|
#borrow(path)
extern "C" fn release_lock_ffi(path : Bytes, token : Int64) -> Int = "moonsearch_release_lock"

///|
fn platform_atomic_replace(
  temporary : String,
  target : String,
) -> Unit raise PersistenceError {
  guard atomic_replace_ffi(
      null_terminated_path(temporary),
      null_terminated_path(target),
    ) ==
    0 else {
    raise PersistenceError::Io("atomic replace failed")
  }
}

///|
fn platform_sync_file(path : String) -> Unit raise PersistenceError {
  guard sync_file_ffi(null_terminated_path(path)) == 0 else {
    raise PersistenceError::Io("file sync failed: \{path}")
  }
}

///|
fn platform_try_acquire_lock(path : String) -> Int64? raise PersistenceError {
  match try_lock_ffi(null_terminated_path(path)) {
    -1L => None
    -2L => raise PersistenceError::Io("writer lock creation failed")
    token => Some(token)
  }
}

///|
fn platform_release_lock(
  path : String,
  token : Int64,
) -> Unit raise PersistenceError {
  match release_lock_ffi(null_terminated_path(path), token) {
    0 => ()
    _ => raise PersistenceError::Io("writer lock creation failed")
  }
}

///|
fn platform_directory_capabilities() -> DirectoryCapabilities {
  {
    atomic_replace: true,
    durable_sync: true,
    exclusive_lock: true,
    read_only_mapping: false,
  }
}