Create a new Cargo project, check the build and the test setup:
Solution
cargo new --lib simple-db
cd simple-db
cargo build
cargo test
Define two enums, one is called Command
and one is called Error
. Command
has 2 variants for the two possible commands. Publish
carries data (the message), Retrieve
does not. Error
is just a list of error kinds. Use #[derive(Eq,PartialEq,Debug)]
for both enums
.
Solution
{{#include ../../exercise-solutions/simple-db/step2/src/lib.rs}}
tl;dr:
message.strip_prefix("FOO ")
returnsSome(remainder)
if the string slicemessage
starts with"FOO "
, otherwise you getNone
message.strip_suffix('\n')
returnsSome(remainder)
if the string slicemessage
ends with'\n'
, otherwise you getNone
.
Note that both functions will take either a string slice, or a character, or will actually even take a function that returns a boolean to tell you whether a character matches or not (we won't use that though).
The proposed logic
-
Check if the string ends with the char
'\n'
- if so, keep the rest of it, otherwise return an error. -
Check if the remainder still contains a
'\n'
- if so, return an error. -
Check if the remainder is empty - if so, return an error.
-
Check if the remainder begins with
"PUBLISH "
- if so, returnOk(Command::Publish(...))
with the payload upconverted to aString
-
Check if the remainder is
"PUBLISH"
- if so, return an error because the mandatory payload is missing. -
Check if the remainder begins with
"RETRIEVE "
- if so, return an error because that command should not have anything after it. -
Check if the remainder is
"RETRIEVE"
- if so, returnOk(Command::Retrieve)
-
Otherwise, return an unknown command error.
Missing, wrongly placed and more than one \n
are errors that occur independent of other errors so it makes sense to handle these cases first. Check the string has a newline at the end with strip_suffix
. If not, that's an Error::IncompleteMessage
. We can assume the pattern will match (that strip_suffix
will return Some(...)
, which is our so-called sunny day scenario) so a let - else makes most sense here - although a match will also work.
Now look for newlines within the remainder using the contains()
method and if you find any, that's an error.
Tip: Introduce a generic variant Command::Command
that temporarily stands for a valid command.
Solution
{{#include ../../exercise-solutions/simple-db/step4a/src/lib.rs:18:27}}
In 4a, we produce a Ok(Command::Command)
if the newlines all check out. Now we want to look for a RETRIEVE command.
If the string is empty, that's an error. If the string is exactly "RETRIEVE"
, that's our command. Otherwise the string starts with "RETRIEVE "
, then that's an UnexpectedPayload error.
Solution
{{#include ../../exercise-solutions/simple-db/step4b/src/lib.rs:18:34}}
Now we want to see if the message starts with "PUBLISH "
, and if so, return a Command::Publish
containing the payload, but converted to a heap-allocted String
so that ownership is passed back to the caller. If not, and the message is equal to "PUBLISH"
, then that's a MissingPayload error.
Solution
{{#include ../../exercise-solutions/simple-db/step4c/src/lib.rs:18:38}}
If all else fails, feel free to copy this solution to play around with it.
Solution
{{#include ../../exercise-solutions/simple-db/step4c/src/lib.rs}}