- Applications often need to create objects from Strings,
and for example, in our application,
we have a String representing year, month, day,
and we need to convert it into a date object, NaiveDate.
So in util.rs, gonna see these functions
to achieve that effect and prompt_for_date
and date_from_str.
Okay, I've written these functions
to help me with date String conversions.
Okay then, so in util.rs, so first of all,
let's give you a quick reminder,
prompt_for_string, and the purpose of the function,
you call the function.
You pass in a message, a prompt message
to tell the user what you want,
and it should return a String.
So it displays the message that we passed in
asking the user to enter something.
I create an empty String object here,
and then in the std::io module, there's a std::in function.
Basically it represents the standard input device.
It's like cin in C++.
It's like Console in C#.
Anyway, from the input device, I read a line, okay?
And I store it in the String.
So it's a bit like scanf in C, if you remember that.
It returns a result, so I unwrap the result, okay?
And I return the String that's been entered.
The String here, which is empty initially,
will be populated by the read_line function,
and then I return that String back to the user.
So that was easy enough.
I've got a similar kinda thing, prompt_for_date.
It takes a message asking the user, you know,
please enter a date, and it's meant to return a NaiveDate.
So it calls the prompt_for_string method
that we just looked at to display the message,
and that gives me back a String.
Okay, so that's a bit of reuse going on there.
I've reused my prompt_for_string function already,
gives me back a String that the user has entered.
I get rid of any white space
in case the user had spaces at the beginning and end,
and then I pass that function into date_from_str down here.
Okay, so it turns out that this date_from_str
is a generally useful function that I might use elsewhere
where you've got a String or a String slice,
and it parses it to give you back a date object, NaiveDate.
So this is the interesting function really, I guess.
Here's a String. Parse that String into NaiveDate.
So it turns out it's just a simple wrapper.
The NaiveDate structure has a parse_from_str
associated function or static function.
You give it a String that contains a stringified date,
and then you tell it the format to expect.
So obviously you can have a look at the documentation
in the NaiveDate structure to see
what the different options are here.
%Y, four-digit year.
%m, month. %d, day.
So it needs to know the format of the incoming String
to see where's the year part, where's the month part,
where's the date part in the incoming String,
and again, I unwrap that result
and I return it back to the code.
All right, so you could argue that this function here,
I could've just called that function directly up here,
and I could've done that,
but sometimes it's nice to just wrap up
some low-level API behind your own function
because that function could in the future evolve
to do something a little bit more complete.
Okay, so I was quite happy with those functions.
So the next thing we're going to look at is how to actually,
in the context of my application scenario,
which is all about visits to Europe
in a 180-day period in Schengen,
how do I actually implement that logic,
the actual scenario, in my application?
And it turns out I had to do quite a lot
of date manipulation.
So we're gonna look at that now,
and this will be the end of our discussions.
This is the only bit that we haven't yet seen.
In menu.rs, we're going to see the options
for basically displaying the dates
within a certain period of interest,
which is the whole point of the demo.
So there's a couple of functions we're gonna look at.
Get_days_prior_to is a function I've written
and display_days_within_period_of_interest.
I'm trying to go for the world record
for the longest function name ever.
So let's have a look at those then.
So in menu.rs, just a quick reminder
just to kind of set the scene,
the menu function, or do_menu,
it displays a list of options to the user,
and we've covered all of these apart from option 2.
How do I calculate the number of days
when I've been away from a specified date?
Right, so if the user chooses option 2,
it calls the display_days_within_period_of_interest,
and it gives you all the visits, it takes in all the visits,
and it calculates how many days you've been away.
So let's have a look at this function now,
display_days_within_period_of_interest.
So here we are.
I'm gonna go through this function in a couple of steps.
I'm just gonna give us a little bit more space as well.
I'm gonna run the application actually before I do this
just to remind you of what that option does.
Let's open up a new terminal.
I run the application, cargo run.
Remember, when the application starts,
it immediately loads in the data from data.txt,
and it builds the collection of visits.
So it's already done all that,
and then it displays the menu,
and its option 2 is what we're interested in,
calculate visit days within period of interest.
So I'll choose option 2, okay?
And you see the first thing it does, I'm in here now.
What reference date do you want to use? Year, month, day.
All right, so I'll enter, let's say,
2023-12-31,
New Year's Eve 2023.
When I press Enter, this is the function
which is currently going to be processing.
So I pass that message into the function.
This function is currently running,
and it's waiting for me to press Enter down here.
When I press Enter, it'll do the work we just looked at,
and it'll give me back a date,
and that date object will represent
the 31st of December, 2023.
So I press Enter.
What it's then gonna do, obviously,
is to display all the results.
So I wanna remind you a couple of things.
First of all, it displays two batches of results, right?
Go back up to the top of the output.
So there are lots of visits here.
The first set of results is chronological.
Okay, so from the date that I specified,
the 31st of December, 2023, it displays the number of days.
It takes the 31st of December,
and it displays the number of days I've been away
for 180 days prior to that date, okay?
So each of these things being displayed here is a Record.
Remember the Record structure?
Let me give you a quick reminder of that.
The Record structure, it says how many days were you away,
and what's the date
that we're talking about, okay?
So if it says if the date
was the 31st of December,
it'll tell me how many days were you away
180 days prior to that, okay?
So from this date here, the 31st of December,
I was away 67 days
in the 180-day period prior to that,
but it doesn't just display the number of days I was away
on the 31st of December.
It also displays the number of days on the 30th of December
and the 29th of December.
The whole point of the application
was not to just give me a single snapshot in time
but to show me a historical record of all dates,
so not only for the date that I've entered
but all other dates, and it basically does all dates.
It'll say, if I scroll down here, for example,
let's just pick a random date, the 5th of November.
That's Bonfire Night, Guy Fawkes Night in the UK.
It says, "In your current calendar on the 11th,
on the 5th of November, you will have been away,
in the 180 days before the 5th of November,
you will have been away for 88 days," all right?
So that's what's going on in my application.
It's gonna build collection of Records.
Each one of these is a Record with a date
and the number of days I've been away
in the 180-day period before that.
So it takes, this date here is the date that I entered,
the 31st of December, 2023, and it basically,
it'll iterate over all dates from now, from that date,
up to and including the current date going backwards, okay?
Each time around the loop,
it'll subtract one day from the date.
First time around the loop,
the date is the 31st of December.
31st of December. That's the current date.
Is the date greater than or equal to the current date?
I mean today. Today here is the, check phone.
It's the 26th of May I'm here.
So it's a bit tricky,
but there's an API to get the current date.
It's the 26th of May.
So this loop is gonna go from the 31st of December
all the way down to the 26th of May,
decrementing as we go.
So if I scroll down here,
it's gonna have to scroll quite a lot.
How far is it gonna go?
There we go, down to today, the 26th of May,
as my phone just told me.
Each time round, and I'll show you how it does this,
it'll say if we're talking about a particular date,
so let's again pick a date at random,
the, oh, I'll tell you what we'll do.
We'll choose the 11th of August.
That was our wedding date back in 1990, the 11th of August.
So let's say that's the date.
It's gonna iterate through all the dates,
but let's say that's the current date it's looking at.
All right, so it'll say prior to that date,
look at all the visits that you'd been away altogether
and tell me in the 180 days prior to that date,
okay, so 180 days prior
to the 11th of August, I was away 74 days.
So it'll tell me how many days I was away, 74 days,
and then it creates a new Record.
It'll create a Record with the date,
like the 11th of August, and it'll say,
"On that date, you were away 74 days."
Okay, so I add that to my vector of all Records,
and then I decrement my date counter
to go back to the previous day.
Okay, so the previous day was the 10th of August,
and it'll say, "Okay then, for the 10th of August,
with all the visits that you had,
how many days were you away
in the 180-day period before then?"
So 180 days before the 10th of August, it'll tell me,
"Oh, it was 74 days away there."
That days, I push it into a new Record again.
Obviously I need to explain how this function works.
I'll get on to that in a moment.
By the time it gets to here,
it has gathered a collection of all the Record objects,
and I then display them in chronological order.
Well, actually, it's reverse chronological order.
For each Record, I iterate over my Records,
and for each Record I display it.
Remember,
my Record type implements the Display trait,
so it can output via the format function.
It can output a Record object as the number of days
and the date that we're talking about,
the number of days and the date that we're talking about.
So that's coming together quite nicely now.
So it displays all the information
in reverse chronological order.
So that was useful for me.
So I can see where the hotspots are.
It looks quite busy around July.
Right, so if I go back, it'll keep on looping
till the bottom of the current date basically,
but then it also displays the same information
in reverse number of days.
Ultimately, I want to be able to see
what's the top number of days I've been away in that period,
so I sort my records.
When you sort the records, it has to compare records to say,
"Is your number of days greater than or less than?"
So sorting, in order to sort a structure type
or a vector of structures,
you have to implement comparability, and I did.
So my Record type implemented the order trait
and the partial order trait, okay?
And that gives me sortability.
It can then compare based on days.
It compares one record against another,
and it's the days that will influence the ordering.
So then, having sorted the records,
these records, this vector is now in time order.
So not in time order, in the number of days order.
So I can then display the records in the number of days,
okay, so the highest number of days appearing
at the top of the list.
It says, "Watch out.
On the 4th of November, in the 180 days prior to that,
you'll have been away 89 days."
That's very close to the 90-day margin for the Brexit rule.
I still can't believe Brexit, to be quite honest.