Polish bounded file read API

This commit is contained in:
Chris Hayduk
2026-05-28 10:35:42 -04:00
parent 9151f707c1
commit bdba10d68e
4 changed files with 77 additions and 11 deletions
@@ -21,7 +21,7 @@ isSymlink: boolean,
/**
* File size in bytes when available.
*/
sizeBytes?: number,
sizeBytes: number | null,
/**
* File creation time in Unix milliseconds when available, otherwise `0`.
*/
@@ -12,11 +12,9 @@ pub struct FsReadFileParams {
/// Absolute path to read.
pub path: AbsolutePathBuf,
/// Optional byte offset for a bounded read.
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional = nullable, type = "number")]
pub offset: Option<u64>,
/// Optional maximum byte length for a bounded read.
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional = nullable, type = "number")]
pub length: Option<u64>,
}
@@ -86,8 +84,7 @@ pub struct FsGetMetadataResponse {
/// Whether the path itself is a symbolic link.
pub is_symlink: bool,
/// File size in bytes when available.
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional = nullable, type = "number")]
#[ts(type = "number | null")]
pub size_bytes: Option<u64>,
/// File creation time in Unix milliseconds when available, otherwise `0`.
#[ts(type = "number")]
@@ -717,6 +717,41 @@ fn fs_get_metadata_response_round_trips_minimal_fields() {
assert_eq!(decoded, response);
}
#[test]
fn fs_get_metadata_response_serializes_unavailable_size_as_null() {
let response = FsGetMetadataResponse {
is_directory: false,
is_file: true,
is_symlink: false,
size_bytes: None,
created_at_ms: 123,
modified_at_ms: 456,
};
let value = serde_json::to_value(&response).expect("serialize fs/getMetadata response");
assert_eq!(
value,
json!({
"isDirectory": false,
"isFile": true,
"isSymlink": false,
"sizeBytes": null,
"createdAtMs": 123,
"modifiedAtMs": 456,
})
);
let decoded = serde_json::from_value::<FsGetMetadataResponse>(json!({
"isDirectory": false,
"isFile": true,
"isSymlink": false,
"createdAtMs": 123,
"modifiedAtMs": 456,
}))
.expect("deserialize fs/getMetadata response without sizeBytes");
assert_eq!(decoded, response);
}
#[test]
fn fs_read_file_response_round_trips_base64_data() {
let response = FsReadFileResponse {
@@ -759,6 +794,31 @@ fn fs_read_file_params_round_trip() {
assert_eq!(decoded, params);
}
#[test]
fn fs_read_file_params_serializes_unbounded_read_with_null_range() {
let params = FsReadFileParams {
path: absolute_path("tmp/example.txt"),
offset: None,
length: None,
};
let value = serde_json::to_value(&params).expect("serialize fs/readFile params");
assert_eq!(
value,
json!({
"path": absolute_path_string("tmp/example.txt"),
"offset": null,
"length": null,
})
);
let decoded = serde_json::from_value::<FsReadFileParams>(json!({
"path": absolute_path_string("tmp/example.txt"),
}))
.expect("deserialize fs/readFile params without range");
assert_eq!(decoded, params);
}
#[test]
fn fs_create_directory_params_round_trip_with_default_recursive() {
let params = FsCreateDirectoryParams {