// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
let tmpdir_seed : @random.Rand = {
let seed = Buffer()
let now = @event_loop.now()
for _ in 0..<4 {
seed.write_int64_le(now)
}
@random.Rand::chacha8(seed=seed.contents())
}
///|
extern "C" fn get_tmp_path_ffi() -> @c_buffer.Buffer = "moonbitlang_async_get_tmp_path"
///|
let tmp_base_path : String = {
let path = get_tmp_path_ffi()
if path.is_null() {
let err = @os_error.errno_to_string(@os_error.get_errno())
abort("failed to obtain directory for temporary files: \{err}")
}
@os_string.decode(path)
}
///|
/// Create a temporary directory in the system-specific temporary file storage
/// (such as `/tmp/`) with prefix `prefix`.
/// `tmpdir` force-create the directory atomically, so it never reuse directory.
///
/// The format of the directory name is:
///
/// ..
///
/// The random number part cotains 32bits of information,
/// so at most 2^32 temporary directories can be created for every process.
/// However, due to collision, `tmpdir` will slow down
/// when a lot of temporary directories are created by the same process.
///
/// The temporary directory name is not cryptographically secure.
pub async fn tmpdir(prefix~ : StringView) -> String {
for _ in 0..<@int.MAX_VALUE {
let hasher = Hasher()
hasher.combine_uint64(tmpdir_seed.uint64())
hasher.combine_int64(@event_loop.now())
let h = hasher.finalize().reinterpret_as_uint()
let path = StringBuilder::new()
path.write_string(tmp_base_path)
path.write_stringview(prefix)
path.write_char('.')
path.write_string(@env_util.current_process.to_string())
path.write_char('.')
path.write_string(h.to_string(radix=16))
let path = path.to_string()
try @event_loop.mkdir(path, mode=0o700, context="@fs.tmpdir()") catch {
@os_error.OSError(_) as err if err.is_EEXIST() => ()
err => raise err
} noraise {
_ => return path
}
}
raise Failure::Failure("failed to create tmp directory: too many attempts")
}