aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFederico Igne <git@federicoigne.com>2020-12-26 16:24:44 +0000
committerFederico Igne <git@federicoigne.com>2021-11-03 18:55:08 +0000
commit8a61a513dad5c22bb5596b8918a2d03755d08d1e (patch)
tree09c1a481fdeed866885b99ddcad2541058be3e5e
parent7ba7cf729752f1b1ff11a02fd8de6410aa448898 (diff)
downloadexercism-8a61a513dad5c22bb5596b8918a2d03755d08d1e.tar.gz
exercism-8a61a513dad5c22bb5596b8918a2d03755d08d1e.zip
[rust] PaaS I/O
-rw-r--r--paasio/.exercism/metadata.json1
-rw-r--r--paasio/.gitignore8
-rw-r--r--paasio/Cargo.toml4
-rw-r--r--paasio/README.md102
-rw-r--r--paasio/src/lib.rs82
-rw-r--r--paasio/tests/paasio.rs192
6 files changed, 389 insertions, 0 deletions
diff --git a/paasio/.exercism/metadata.json b/paasio/.exercism/metadata.json
new file mode 100644
index 0000000..e6e8012
--- /dev/null
+++ b/paasio/.exercism/metadata.json
@@ -0,0 +1 @@
{"track":"rust","exercise":"paasio","id":"6ac29a8082a94a0a91b43185846bcfe7","url":"https://exercism.io/my/solutions/6ac29a8082a94a0a91b43185846bcfe7","handle":"dyamon","is_requester":true,"auto_approve":false} \ No newline at end of file
diff --git a/paasio/.gitignore b/paasio/.gitignore
new file mode 100644
index 0000000..db7f315
--- /dev/null
+++ b/paasio/.gitignore
@@ -0,0 +1,8 @@
1# Generated by Cargo
2# will have compiled files and executables
3/target/
4**/*.rs.bk
5
6# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
8Cargo.lock
diff --git a/paasio/Cargo.toml b/paasio/Cargo.toml
new file mode 100644
index 0000000..83a8e73
--- /dev/null
+++ b/paasio/Cargo.toml
@@ -0,0 +1,4 @@
1[package]
2edition = "2018"
3name = "paasio"
4version = "0.0.0"
diff --git a/paasio/README.md b/paasio/README.md
new file mode 100644
index 0000000..03e5a4d
--- /dev/null
+++ b/paasio/README.md
@@ -0,0 +1,102 @@
1# PaaS I/O
2
3Report network IO statistics.
4
5You are writing a [PaaS][], and you need a way to bill customers based
6on network and filesystem usage.
7
8Create a wrapper for network connections and files that can report IO
9statistics. The wrapper must report:
10
11- The total number of bytes read/written.
12- The total number of read/write operations.
13
14[PaaS]: http://en.wikipedia.org/wiki/Platform_as_a_service
15
16## Abstraction over Networks and Files
17
18Network and file operations are implemented in terms of the [`io::Read`][read] and [`io::Write`][write] traits. It will therefore be necessary to implement those traits for your types.
19
20[read]: https://doc.rust-lang.org/std/io/trait.Read.html
21[write]: https://doc.rust-lang.org/std/io/trait.Write.html
22
23
24## Rust Installation
25
26Refer to the [exercism help page][help-page] for Rust installation and learning
27resources.
28
29## Writing the Code
30
31Execute the tests with:
32
33```bash
34$ cargo test
35```
36
37All but the first test have been ignored. After you get the first test to
38pass, open the tests source file which is located in the `tests` directory
39and remove the `#[ignore]` flag from the next test and get the tests to pass
40again. Each separate test is a function with `#[test]` flag above it.
41Continue, until you pass every test.
42
43If you wish to run all ignored tests without editing the tests source file, use:
44
45```bash
46$ cargo test -- --ignored
47```
48
49To run a specific test, for example `some_test`, you can use:
50
51```bash
52$ cargo test some_test
53```
54
55If the specific test is ignored use:
56
57```bash
58$ cargo test some_test -- --ignored
59```
60
61To learn more about Rust tests refer to the [online test documentation][rust-tests]
62
63Make sure to read the [Modules][modules] chapter if you
64haven't already, it will help you with organizing your files.
65
66## Further improvements
67
68After you have solved the exercise, please consider using the additional utilities, described in the [installation guide](https://exercism.io/tracks/rust/installation), to further refine your final solution.
69
70To format your solution, inside the solution directory use
71
72```bash
73cargo fmt
74```
75
76To see, if your solution contains some common ineffective use cases, inside the solution directory use
77
78```bash
79cargo clippy --all-targets
80```
81
82## Submitting the solution
83
84Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer.
85
86## Feedback, Issues, Pull Requests
87
88The [exercism/rust](https://github.com/exercism/rust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the rust track team are happy to help!
89
90If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md).
91
92[help-page]: https://exercism.io/tracks/rust/learning
93[modules]: https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html
94[cargo]: https://doc.rust-lang.org/book/ch14-00-more-about-cargo.html
95[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html
96
97## Source
98
99Brian Matsuo [https://github.com/bmatsuo](https://github.com/bmatsuo)
100
101## Submitting Incomplete Solutions
102It's possible to submit an incomplete solution so you can see how others have completed the exercise.
diff --git a/paasio/src/lib.rs b/paasio/src/lib.rs
new file mode 100644
index 0000000..71f4c9d
--- /dev/null
+++ b/paasio/src/lib.rs
@@ -0,0 +1,82 @@
1use std::io::{Read, Result, Write};
2
3pub struct ReadStats<R> {
4 source: R,
5 reads: usize,
6 bytes: usize,
7}
8
9impl<R: Read> ReadStats<R> {
10 pub fn new(wrapped: R) -> ReadStats<R> {
11 ReadStats {
12 source: wrapped,
13 reads: 0,
14 bytes: 0,
15 }
16 }
17
18 pub fn get_ref(&self) -> &R {
19 &self.source
20 }
21
22 pub fn bytes_through(&self) -> usize {
23 self.bytes
24 }
25
26 pub fn reads(&self) -> usize {
27 self.reads
28 }
29}
30
31impl<R: Read> Read for ReadStats<R> {
32 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
33 let bytes = self.source.read(buf)?;
34 self.reads += 1;
35 self.bytes += bytes;
36 Ok(bytes)
37 }
38}
39
40pub struct WriteStats<W> {
41 sink: W,
42 writes: usize,
43 bytes: usize,
44}
45
46impl<W: Write> WriteStats<W> {
47 // _wrapped is ignored because W is not bounded on Debug or Display and therefore
48 // can't be passed through format!(). For actual implementation you will likely
49 // wish to remove the leading underscore so the variable is not ignored.
50 pub fn new(wrapped: W) -> WriteStats<W> {
51 WriteStats {
52 sink: wrapped,
53 writes: 0,
54 bytes: 0,
55 }
56 }
57
58 pub fn get_ref(&self) -> &W {
59 &self.sink
60 }
61
62 pub fn bytes_through(&self) -> usize {
63 self.bytes
64 }
65
66 pub fn writes(&self) -> usize {
67 self.writes
68 }
69}
70
71impl<W: Write> Write for WriteStats<W> {
72 fn write(&mut self, buf: &[u8]) -> Result<usize> {
73 let bytes = self.sink.write(buf)?;
74 self.writes += 1;
75 self.bytes += bytes;
76 Ok(bytes)
77 }
78
79 fn flush(&mut self) -> Result<()> {
80 self.sink.flush()
81 }
82}
diff --git a/paasio/tests/paasio.rs b/paasio/tests/paasio.rs
new file mode 100644
index 0000000..6b44199
--- /dev/null
+++ b/paasio/tests/paasio.rs
@@ -0,0 +1,192 @@
1/// test a few read scenarios
2macro_rules! test_read {
3 ($(#[$attr:meta])* $modname:ident ($input:expr, $len:expr)) => {
4 mod $modname {
5 use std::io::{Read, BufReader};
6 use paasio::*;
7
8 const CHUNK_SIZE: usize = 2;
9
10 $(#[$attr])*
11 #[test]
12 fn test_read_passthrough() {
13 let data = $input;
14 let size = $len(&data);
15 let mut reader = ReadStats::new(data);
16
17 let mut buffer = Vec::with_capacity(size);
18 let qty_read = reader.read_to_end(&mut buffer);
19
20 assert!(qty_read.is_ok());
21 assert_eq!(size, qty_read.unwrap());
22 assert_eq!(size, buffer.len());
23 // 2: first to read all the data, second to check that
24 // there wasn't any more pending data which simply didn't
25 // fit into the existing buffer
26 assert_eq!(2, reader.reads());
27 assert_eq!(size, reader.bytes_through());
28 }
29
30 $(#[$attr])*
31 #[test]
32 fn test_read_chunks() {
33 let data = $input;
34 let size = $len(&data);
35 let mut reader = ReadStats::new(data);
36
37 let mut buffer = [0_u8; CHUNK_SIZE];
38 let mut chunks_read = 0;
39 while reader.read(&mut buffer[..]).unwrap_or_else(|_| panic!("read failed at chunk {}", chunks_read+1)) > 0 {
40 chunks_read += 1;
41 }
42
43 assert_eq!(size / CHUNK_SIZE + std::cmp::min(1, size % CHUNK_SIZE), chunks_read);
44 // we read once more than the number of chunks, because the final
45 // read returns 0 new bytes
46 assert_eq!(1+chunks_read, reader.reads());
47 assert_eq!(size, reader.bytes_through());
48 }
49
50 $(#[$attr])*
51 #[test]
52 fn test_read_buffered_chunks() {
53 let data = $input;
54 let size = $len(&data);
55 let mut reader = BufReader::new(ReadStats::new(data));
56
57 let mut buffer = [0_u8; CHUNK_SIZE];
58 let mut chunks_read = 0;
59 while reader.read(&mut buffer[..]).unwrap_or_else(|_| panic!("read failed at chunk {}", chunks_read+1)) > 0 {
60 chunks_read += 1;
61 }
62
63 assert_eq!(size / CHUNK_SIZE + std::cmp::min(1, size % CHUNK_SIZE), chunks_read);
64 // the BufReader should smooth out the reads, collecting into
65 // a buffer and performing only two read operations:
66 // the first collects everything into the buffer,
67 // and the second ensures that no data remains
68 assert_eq!(2, reader.get_ref().reads());
69 assert_eq!(size, reader.get_ref().bytes_through());
70 }
71 }
72 };
73}
74
75/// test a few write scenarios
76macro_rules! test_write {
77 ($(#[$attr:meta])* $modname:ident ($input:expr, $len:expr)) => {
78 mod $modname {
79 use std::io::{self, Write, BufWriter};
80 use paasio::*;
81
82 const CHUNK_SIZE: usize = 2;
83 $(#[$attr])*
84 #[test]
85 fn test_write_passthrough() {
86 let data = $input;
87 let size = $len(&data);
88 let mut writer = WriteStats::new(Vec::with_capacity(size));
89 let written = writer.write(data);
90 assert!(written.is_ok());
91 assert_eq!(size, written.unwrap());
92 assert_eq!(size, writer.bytes_through());
93 assert_eq!(1, writer.writes());
94 assert_eq!(data, writer.get_ref().as_slice());
95 }
96
97 $(#[$attr])*
98 #[test]
99 fn test_sink_oneshot() {
100 let data = $input;
101 let size = $len(&data);
102 let mut writer = WriteStats::new(io::sink());
103 let written = writer.write(data);
104 assert!(written.is_ok());
105 assert_eq!(size, written.unwrap());
106 assert_eq!(size, writer.bytes_through());
107 assert_eq!(1, writer.writes());
108 }
109
110 $(#[$attr])*
111 #[test]
112 fn test_sink_windowed() {
113 let data = $input;
114 let size = $len(&data);
115 let mut writer = WriteStats::new(io::sink());
116
117 let mut chunk_count = 0;
118 for chunk in data.chunks(CHUNK_SIZE) {
119 chunk_count += 1;
120 let written = writer.write(chunk);
121 assert!(written.is_ok());
122 assert_eq!(CHUNK_SIZE, written.unwrap());
123 }
124 assert_eq!(size, writer.bytes_through());
125 assert_eq!(chunk_count, writer.writes());
126 }
127
128 $(#[$attr])*
129 #[test]
130 fn test_sink_buffered_windowed() {
131 let data = $input;
132 let size = $len(&data);
133 let mut writer = BufWriter::new(WriteStats::new(io::sink()));
134
135 for chunk in data.chunks(CHUNK_SIZE) {
136 let written = writer.write(chunk);
137 assert!(written.is_ok());
138 assert_eq!(CHUNK_SIZE, written.unwrap());
139 }
140 // at this point, nothing should have yet been passed through to
141 // our writer
142 assert_eq!(0, writer.get_ref().bytes_through());
143 assert_eq!(0, writer.get_ref().writes());
144
145 // after flushing, everything should pass through in one go
146 assert!(writer.flush().is_ok());
147 assert_eq!(size, writer.get_ref().bytes_through());
148 assert_eq!(1, writer.get_ref().writes());
149 }
150 }
151 };
152}
153
154#[test]
155fn test_create_stats() {
156 let mut data: Vec<u8> = Vec::new();
157 let _ = paasio::ReadStats::new(data.as_slice());
158 let _ = paasio::WriteStats::new(data.as_mut_slice());
159}
160
161test_read!(read_string (
162 "Twas brillig, and the slithy toves/Did gyre and gimble in the wabe:/All mimsy were the borogoves,/And the mome raths outgrabe.".as_bytes(),
163 |d: &[u8]| d.len()
164));
165test_write!(write_string (
166 "Beware the Jabberwock, my son!/The jaws that bite, the claws that catch!/Beware the Jubjub bird, and shun/The frumious Bandersnatch!".as_bytes(),
167 |d: &[u8]| d.len()
168));
169
170test_read!(read_byte_literal(
171 &[1_u8, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144][..],
172 |d: &[u8]| d.len()
173));
174test_write!(write_byte_literal(
175 &[2_u8, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61,][..],
176 |d: &[u8]| d.len()
177));
178
179test_read!(read_file(
180 ::std::fs::File::open("README.md").expect("readme must be present"),
181 |f: &::std::fs::File| f.metadata().expect("metadata must be present").len() as usize
182));
183
184#[test]
185fn read_stats_by_ref_returns_wrapped_reader() {
186 use paasio::ReadStats;
187
188 let input =
189 "Why, sometimes I've believed as many as six impossible things before breakfast".as_bytes();
190 let reader = ReadStats::new(input);
191 assert_eq!(reader.get_ref(), &input);
192}