1use core::{fmt, ops};
2use std::io;
3
4use crate::{
5 PyObject, PyObjectRef, PyResult, VirtualMachine,
6 builtins::{PyBaseExceptionRef, PyBytes, PyStr},
7 common::ascii,
8};
9
10pub trait Write {
11 type Error;
12
13 fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), Self::Error>;
14}
15
16#[repr(transparent)]
17pub struct IoWriter<T>(pub T);
18
19impl<T> IoWriter<T> {
20 pub fn from_ref(x: &mut T) -> &mut Self {
21 unsafe { &mut *(x as *mut T as *mut Self) }
23 }
24}
25
26impl<T> ops::Deref for IoWriter<T> {
27 type Target = T;
28
29 fn deref(&self) -> &T {
30 &self.0
31 }
32}
33
34impl<T> ops::DerefMut for IoWriter<T> {
35 fn deref_mut(&mut self) -> &mut T {
36 &mut self.0
37 }
38}
39
40impl<W> Write for IoWriter<W>
41where
42 W: io::Write,
43{
44 type Error = io::Error;
45
46 fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {
47 <W as io::Write>::write_fmt(&mut self.0, args)
48 }
49}
50
51impl Write for String {
52 type Error = fmt::Error;
53
54 fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
55 <Self as fmt::Write>::write_fmt(self, args)
56 }
57}
58
59pub struct PyWriter<'vm>(pub PyObjectRef, pub &'vm VirtualMachine);
60
61impl Write for PyWriter<'_> {
62 type Error = PyBaseExceptionRef;
63
64 fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), Self::Error> {
65 let Self(obj, vm) = self;
66
67 vm.call_method(obj, "write", (args.to_string(),)).map(drop)
68 }
69}
70
71pub fn file_readline(obj: &PyObject, size: Option<usize>, vm: &VirtualMachine) -> PyResult {
72 let args = size.map_or_else(Vec::new, |size| vec![vm.ctx.new_int(size).into()]);
73 let ret = vm.call_method(obj, "readline", args)?;
74 let eof_err = || {
75 vm.new_exception(
76 vm.ctx.exceptions.eof_error.to_owned(),
77 vec![vm.ctx.new_str(ascii!("EOF when reading a line")).into()],
78 )
79 };
80
81 let ret = match_class!(match ret {
82 s @ PyStr => {
83 let s_wtf8 = s.as_wtf8();
85 if s_wtf8.is_empty() {
86 return Err(eof_err());
87 }
88
89 if s_wtf8.as_bytes().last() == Some(&b'\n') {
91 let no_nl = &s_wtf8[..s_wtf8.len() - 1];
92 vm.ctx.new_str(no_nl).into()
93 } else {
94 s.into()
95 }
96 }
97 b @ PyBytes => {
98 let buf = b.as_bytes();
99 if buf.is_empty() {
100 return Err(eof_err());
101 }
102
103 if buf.last() == Some(&b'\n') {
104 vm.ctx.new_bytes(buf[..buf.len() - 1].to_owned()).into()
105 } else {
106 b.into()
107 }
108 }
109 _ => return Err(vm.new_type_error("object.readline() returned non-string")),
110 });
111 Ok(ret)
112}