Make Stream::Null behave like /dev/null

This commit is contained in:
Emilie Burgun
2025-02-02 17:38:59 +01:00
parent fdc35f8b40
commit 7108e87e92
2 changed files with 45 additions and 33 deletions

View File

@@ -509,6 +509,10 @@ impl Machine {
.insert(atom!("user_error"), self.user_error); .insert(atom!("user_error"), self.user_error);
self.indices.streams.insert(self.user_error); self.indices.streams.insert(self.user_error);
self.indices
.stream_aliases
.insert(atom!("null_stream"), Stream::Null(StreamOptions::default()));
} }
#[inline(always)] #[inline(always)]

View File

@@ -836,13 +836,13 @@ impl Read for Stream {
ErrorKind::PermissionDenied, ErrorKind::PermissionDenied,
StreamError::ReadFromOutputStream, StreamError::ReadFromOutputStream,
)), )),
Stream::OutputFile(_) Stream::Null(_) => Ok(buf.len()),
| Stream::StandardError(_) Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) => {
| Stream::StandardOutput(_) Err(std::io::Error::new(
| Stream::Null(_) => Err(std::io::Error::new( ErrorKind::PermissionDenied,
ErrorKind::PermissionDenied, StreamError::ReadFromOutputStream,
StreamError::ReadFromOutputStream, ))
)), }
} }
} }
} }
@@ -864,13 +864,10 @@ impl Write for Stream {
ErrorKind::PermissionDenied, ErrorKind::PermissionDenied,
StreamError::WriteToInputStream, StreamError::WriteToInputStream,
)), )),
Stream::StaticString(_) Stream::Null(_) => Ok(buf.len()),
| Stream::Readline(_) Stream::StaticString(_) | Stream::Readline(_) | Stream::InputFile(..) => Err(
| Stream::InputFile(..) std::io::Error::new(ErrorKind::PermissionDenied, StreamError::WriteToInputStream),
| Stream::Null(_) => Err(std::io::Error::new( ),
ErrorKind::PermissionDenied,
StreamError::WriteToInputStream,
)),
} }
} }
@@ -890,13 +887,10 @@ impl Write for Stream {
ErrorKind::PermissionDenied, ErrorKind::PermissionDenied,
StreamError::FlushToInputStream, StreamError::FlushToInputStream,
)), )),
Stream::StaticString(_) Stream::Null(_) => Ok(()),
| Stream::Readline(_) Stream::StaticString(_) | Stream::Readline(_) | Stream::InputFile(_) => Err(
| Stream::InputFile(_) std::io::Error::new(ErrorKind::PermissionDenied, StreamError::FlushToInputStream),
| Stream::Null(_) => Err(std::io::Error::new( ),
ErrorKind::PermissionDenied,
StreamError::FlushToInputStream,
)),
} }
} }
} }
@@ -1144,6 +1138,7 @@ impl Stream {
} }
} }
} }
Stream::Null(_) => AtEndOfStream::At,
#[cfg(feature = "http")] #[cfg(feature = "http")]
Stream::HttpRead(stream_layout) => { Stream::HttpRead(stream_layout) => {
if stream_layout if stream_layout
@@ -1356,7 +1351,8 @@ impl Stream {
| Stream::Byte(_) | Stream::Byte(_)
| Stream::Readline(_) | Stream::Readline(_)
| Stream::StaticString(_) | Stream::StaticString(_)
| Stream::InputFile(..) => true, | Stream::InputFile(..)
| Stream::Null(_) => true,
_ => false, _ => false,
} }
} }
@@ -1372,7 +1368,8 @@ impl Stream {
| Stream::StandardOutput(_) | Stream::StandardOutput(_)
| Stream::NamedTcp(..) | Stream::NamedTcp(..)
| Stream::Byte(_) | Stream::Byte(_)
| Stream::OutputFile(..) => true, | Stream::OutputFile(..)
| Stream::Null(_) => true,
_ => false, _ => false,
} }
} }
@@ -1607,7 +1604,7 @@ impl MachineState {
debug_assert_eq!(arity, 0); debug_assert_eq!(arity, 0);
return match stream_aliases.get(&name) { return match stream_aliases.get(&name) {
Some(stream) if !stream.is_null_stream() => Ok(*stream), Some(stream) => Ok(*stream),
_ => { _ => {
let stub = functor_stub(caller, arity); let stub = functor_stub(caller, arity);
let addr = atom_as_cell!(name); let addr = atom_as_cell!(name);
@@ -1625,7 +1622,7 @@ impl MachineState {
debug_assert_eq!(arity, 0); debug_assert_eq!(arity, 0);
return match stream_aliases.get(&name) { return match stream_aliases.get(&name) {
Some(stream) if !stream.is_null_stream() => Ok(*stream), Some(stream) => Ok(*stream),
_ => { _ => {
let stub = functor_stub(caller, arity); let stub = functor_stub(caller, arity);
let addr = atom_as_cell!(name); let addr = atom_as_cell!(name);
@@ -1639,11 +1636,10 @@ impl MachineState {
(HeapCellValueTag::Cons, ptr) => { (HeapCellValueTag::Cons, ptr) => {
match_untyped_arena_ptr!(ptr, match_untyped_arena_ptr!(ptr,
(ArenaHeaderTag::Stream, stream) => { (ArenaHeaderTag::Stream, stream) => {
return if stream.is_null_stream() { if stream.is_null_stream() {
Err(self.open_permission_error(HeapCellValue::from(stream), caller, arity)) unreachable!("Null streams have no Cons representation");
} else { }
Ok(stream) return Ok(stream);
};
} }
(ArenaHeaderTag::Dropped, _value) => { (ArenaHeaderTag::Dropped, _value) => {
let stub = functor_stub(caller, arity); let stub = functor_stub(caller, arity);
@@ -1936,7 +1932,11 @@ mod test {
let results = machine.run_query("get_code(C).").collect::<Vec<_>>(); let results = machine.run_query("get_code(C).").collect::<Vec<_>>();
assert_eq!(results.len(), 1); assert_eq!(results.len(), 1);
assert!(results[0].is_err()); assert!(
results[0].is_ok(),
"Expected read to succeed, got {:?}",
results[0]
);
} }
#[test] #[test]
@@ -1966,7 +1966,11 @@ mod test {
let results = machine.run_query("write(hello).").collect::<Vec<_>>(); let results = machine.run_query("write(hello).").collect::<Vec<_>>();
assert_eq!(results.len(), 1); assert_eq!(results.len(), 1);
assert!(results[0].is_err()); assert!(
results[0].is_ok(),
"Expected write to succeed, got {:?}",
results[0]
);
} }
/// A variant of the [`write_null_stream`] that tries to write to a (null) input stream. /// A variant of the [`write_null_stream`] that tries to write to a (null) input stream.
@@ -1982,6 +1986,10 @@ mod test {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
assert_eq!(results.len(), 1); assert_eq!(results.len(), 1);
assert!(results[0].is_err()); assert!(
results[0].is_ok(),
"Expected write to succeed, got {:?}",
results[0]
);
} }
} }