- In this lesson,
we're going to investigate a complete Rust application
that parses a CSV file.
So this will give us a chance
to consolidate the understanding
of application structure in Rust in terms
of how to break the application down into separate modules
and a closer look at the syntax of Rust.
We've already covered that in some detail
but to see it applied
in a larger application will will be nice.
And also a closer look at some
of the libraries available in Rust and some techniques.
We've had a touch on some of these topics
but we're gonna delve deeper
into some particular libraries here,
such as file handling and date handling,
common requirements in lots of applications.
So I'm going to just lead you
through the application functionality.
I'm going to run the application.
It's in this project, lesson16_csv_parser.
I'm just gonna show a couple of files initially.
The application is gonna be reading data
from a file called data.txt.
It's a CSV file, comma-separated value.
And I'm gonna show you the structure of the file
so you can understand the scenario, the application
and then I'll just show you the main code.
It doesn't do much, it just kind of kickstarts lots
of other parts of the application
but it'll give you an idea of the overall shape.
I'll run the application then
and I'll go through the functionality
so you can understand what it is we're going to achieve
when we look at the code in detail
for the rest of the lesson.
Okay, so let's take a look at the code.
Okay, so in the src folder, note this.
I've got the main file, which is the entry point
and then I've got four other modules, four other files
to split the application functionality into chunks.
Fh.rs contains all the file handling functionality.
Menu.rs displays a menu of options to the user.
You could have guessed that I suppose.
Types.rs define some of the structures
that I'm gonna use across the application.
And then util.rs, I mean I've chosen these names myself.
You can call 'em anything you like.
Util.rs contains utility functions that are generally useful
across the whole application
and obviously, main is the entry point.
So in an application that has multiple files,
the first thing you do in main is to declare submodules.
Remember the mod keyword declares a module
and Rust then has to find the code for that module.
And by default, it'll look for a file called types.rs.
So this statement will kind of include the code
for types.rs to define my data types
and then I define a module for util,
which will obviously pull in util.rs
with my utility code and file handling and menu handling.
Now, probably the first new note here
is that the main function's very small.
This is quite common in professional applications.
The main function doesn't really do much.
It kickstarts other functionality located elsewhere.
So generally, our main function would be a couple of lines,
literally something like this.
So let me give you like a broad overview
of the intent before we look at the detail later on.
In my util module,
the util module that I declared there,
in my util module,
there's a function, parse_command_line,
which we'll have a look at later on.
The idea when you run the application, optionally,
you can run the application and you can specify cargo run
and you can specify the name
of the text file that you'd like to load in.
If the user hasn't specified the text file
on the command line, then it defaults to data.txt.
So let's take a look at that now.
Data.txt this here.
Let me tell you a story about a country that decided
to leave the European Union in 2016.
Brexit.
I live in the UK, and before Brexit,
we could travel to Europe completely unimpinged,
complete freedom of movement.
Well, those days have gone, we've Brexited now.
One of the consequences
is that you can only travel to a European,
Schengen country for a maximum of 90 days
in any 180-day period.
And I travel away to Europe a lot
with my work and holiday.
Living in the UK, Europe is our main holiday location.
So I started to feel a bit anxious
that I might accidentally be away
for more than 90 days in 180-day period.
And I'm not sure what would happen then.
I don't know if I wanna find out.
So I wrote myself a Rust program
and here it is to keep track of all my visits to Europe
so that I can keep track of how many days I've been away.
It's a rolling thing.
So for any day, you have to look back
at the previous 180 days and you must not have traveled more
than 90 in that window.
Tomorrow, the window moves on a day, okay?
So it's always looking back at the previous 180 days.
So this actually, this date here,
it was my first visit overseas after COVID lockdown.
I was amazed to find out
that this course was actually going ahead in person.
I remember it so clearly.
I was overjoyed to actually get the chance to travel again,
like a lot of people.
It was on the 30th of August 2021.
And so the start date is the date when I arrived
in the country.
It was in Norway, and the end date is exclusive.
So I was in Norway on the 7th of September
but this date is the day after.
And like a lot of things in programming,
the lower index or the lower bound is inclusive
and the upper bound is exclusive.
So if you know the date
after you came back and the date when you went,
you can calculate how many days you were there.
I've got a description of what I was doing as well.
I was teaching C++ in Kongsberg.
So each line is basically a different visit
into the Schengen zone and it's mainly Norway.
I do most of my work in Norway and I love Norway.
I really do.
But we went to a trip, a holiday to Malta in November 2021,
which is our first holiday,
like a holiday holiday for about two years abroad.
And it was magical,
even though it was starting to get a bit chilly
in November in Malta, we went to Valletta,
I'd really recommend it.
So anyway, there we go.
Various different...
Now you know my calendar.
Up to and including a course that's currently scheduled.
Oh, that's my birthday, the 3rd of December in Copenhagen.
That's another beautiful city, a city of spires.
So that's my file, data.txt.
And when I run the application, it looks to see if the user
from the command line has specified a different file name.
And if not, it'll just default to reading in data.txt.
So that's the file name that's gonna get read in.
And all my file handling functionality
I've put into my Fh module.
So in the Fh module,
I've got a function called read_visits_from_file.
I give it the file name,
the file name that I specified up here
and it'll give me back a collection of visits.
I've got a structure called visit defined in types.rs.
We'll have a look at that later.
It says what date you visited, what date you came back
and what were you doing there?
Teaching C++ in plus plus in Kongsberg.
So that's like a vector of visits.
And then I displayed a menu.
My menu module has a do_menu function.
And you'll see how this works in the moment.
It displays a list of options like calculate dates in visit.
It gives you a chance to add another visit
if you've just booked another trip or you can quit.
In fact, the do_menu function, it loops around,
allowing you to choose items repeatedly from the menu.
But when you say quit, the function returns.
And in fact, there are two different ways of quitting
in my example.
You can quit without saving any changes
or you can quit asking for those changes to be saved.
And this function, as we'll see, returns a true or false.
If it returns true, it means the user chose
to quit the application and save the changes they've made
to the itinerary.
So if this function returns true, then I save the visits
that have been updated back to the file.
And again, write_visits_to_file is defined in my Fh module.
So over the next few sections,
we're gonna delve into the file handling.
We talk about utility handling as well
and dates, times, and command line parsing.
So you'll see how it all works, but you can kind
of get a framework for what we're trying to achieve here.
So I'm gonna run the application, I'm gonna play with it
for a few minutes and you can as well
just to get a flavor for what's going on.