summaryrefslogtreecommitdiffstats
path: root/tokio-fs/src/write.rs
blob: 701d2d3b9902b5c69c59ecfbb1a0ac3ccc58bb7d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use crate::{file, File};

use tokio_io::AsyncWrite;

use futures_core::ready;
use std::future::Future;
use std::pin::Pin;
use std::task::Context;
use std::task::Poll;
use std::{fmt, io, mem, path::Path};

/// Creates a future that will open a file for writing and write the entire
/// contents of `contents` to it.
///
/// This is the async equivalent of `std::fs::write`.
///
/// # Examples
///
/// ```no_run
/// use tokio::prelude::Future;
///
/// let buffer = b"Hello world!";
/// let task = tokio::fs::write("foo.txt", buffer).map(|data| {
///     // `data` has now been written to foo.txt. The buffer is being
///     // returned so it can be used for other things.
///     println!("foo.txt now had {} bytes written to it", data.len());
/// }).map_err(|e| {
///     // handle errors
///     eprintln!("IO error: {:?}", e);
/// });
///
/// tokio::run(task);
/// ```
pub fn write<P, C: AsRef<[u8]> + Unpin>(path: P, contents: C) -> WriteFile<P, C>
where
    P: AsRef<Path> + Send + Unpin + 'static,
{
    WriteFile {
        state: State::Create(File::create(path), Some(contents)),
    }
}

/// A future used to open a file for writing and write the entire contents
/// of some data to it.
#[derive(Debug)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct WriteFile<P: AsRef<Path> + Send + Unpin + 'static, C: AsRef<[u8]> + Unpin> {
    state: State<P, C>,
}

#[derive(Debug)]
enum State<P: AsRef<Path> + Send + Unpin + 'static, C: AsRef<[u8]> + Unpin> {
    Create(file::CreateFuture<P>, Option<C>),
    Writing { f: File, buf: C, pos: usize },
    Empty,
}

fn zero_write() -> io::Error {
    io::Error::new(io::ErrorKind::WriteZero, "zero-length write")
}

impl<P: AsRef<Path> + Send + Unpin + 'static, C: AsRef<[u8]> + Unpin + fmt::Debug> Future
    for WriteFile<P, C>
{
    type Output = io::Result<C>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let inner = Pin::get_mut(self);
        match &mut inner.state {
            State::Create(create_file, contents) => {
                let file = ready!(Pin::new(create_file).poll(cx))?;
                let contents = contents.take().unwrap();
                let new_state = State::Writing {
                    f: file,
                    buf: contents,
                    pos: 0,
                };
                mem::replace(&mut inner.state, new_state);
                // We just entered the Write state, need to poll it before returning.
                return Pin::new(inner).poll(cx);
            }
            State::Empty => panic!("poll a WriteFile after it's done"),
            _ => {}
        }

        match mem::replace(&mut inner.state, State::Empty) {
            State::Writing {
                mut f,
                buf,
                mut pos,
            } => {
                let buf_ref = buf.as_ref();
                while pos < buf_ref.len() {
                    let n = ready!(Pin::new(&mut f).poll_write(cx, &buf_ref[pos..]))?;
                    pos += n;
                    if n == 0 {
                        return Poll::Ready(Err(zero_write()));
                    }
                }
                Poll::Ready(Ok(buf))
            }
            _ => panic!(),
        }
    }
}