Transcript from the "Projector CLI App in Rust" Lesson
>> We get to move on to rust at this point, so if you've completely forgotten where we were, we just got done building the four project or operations in go which is get value, get all values, set value and remove value. We try to be a little bit more idiomatic about it in the sense that when you get a value, if the value is missing, it returns a false plus an empty string.
[00:00:20] If there is a value returns true plus the string itself, so feels pretty good. Now we're gonna go to rust, obviously, the greatest language of all time. If you use rust you get to tell other people you use rust, it is one of the big benefits of doing it, so make sure you really want to pay attention to this next part cuz then you get to tell people about it.
[00:00:40] And really, what's the point of all this programming if you can't tell the people why their language isn't as good as yours? So let's go over to our projector rust, go in here and let's build our projector. So exact same file structure, that we have. Remember, when you add a file to rust, what do we got to do, well we can create the file.
[00:01:01] Then we got to go over to lib, we got to make sure it's publicly available projector, there we go, awesome. It's gonna say hey, I did not find this file. That's because we did save the file protector rs. I'm gonna save the file. Now the file is there.
[00:01:16] And that's my list of fast jumps [INAUDIBLE] put there just like that, there we go. And it's available awesome, there we go. We're ready to actually do the programming. So let's just do this kind of one step at a time, just like we've been doing before. So for the TypeScript version and for the go one we read from file obviously, I did the reading from file at the end for the go version, but at the beginning of TypeScript.
[00:01:42] So, let's just do it the TypeScript way and start off by creating a projector from a config. So of course, before we can even do that, we probably need to create something pretty darn similar to what we have in both go and in TypeScript. So I'm gonna create a struct data and this is gonna have a projector property.
[00:02:01] Let's make it a pub, right, and this needs to be a map path buff because remember they use path buffs have a map string string. So let's create that now, in rust, they have something called a hash map. Now, hash maps are, the API can be a bit wonky at first, I still feel like I haven't quite mastered this API.
[00:02:24] Which is kind of weird to say because usually hash are well, you only have three methods, get set and remove, what do you mean mass. [INAUDIBLE] This is a little different, a little different than your traditional map. This isn't your grandmother Grace Hoppers map, okay, this is a different map here.
[00:02:40] So let's kind of play around with it. So of course we got to do path buff as the item and then a HashMap of string string, right, awesome. This makes sense, I think everybody's happy with that. We can even do something that's kind of nice. We could technically create this as a types we don't have the type all this out.
[00:03:02] We could just have ourselves have a type HashMap equals a HashMap of string string, right because we know we're gonna return that at some point. So we could have that right there. Always kind of like a fun thing to be able to use in rust that you can kind of do these little type aliasing out, add some things that are a little bit more convenient.
[00:03:23] We're not gonna do that, we're going to just type it all out great for your brain. It's like eating your vegetables when you're first learning all these things. So there we go. We have our HashMap, we have our path buff, we have our data. This is the thing we want to be saving.
[00:03:35] But we're gonna run into a bit of a problem here in about a minute and a half. And so we're just gonna solve it now which is how do you handle JSON in Rust. Well, it's actually quite simple. So first let me just skip past all this cuz this is all the Go stuff.
[00:03:51] Avert your eyes if you can't look at Go and you can, yeah, look at that. I created that meme. This was what I meant to not skip over. It's Python mixed with Rust, mixed with Go. It's very cursed. It's very terrifying. All right, there we go. We're gonna get rusty.
[00:04:08] We're right here, of course, we need to install one program. It's very, very important or one library. It's very, very important. It's technically two libraries, so we're going to do that. So let's go to the command line and let's do a cargo add. We're gonna do ser'de, that's how I pronounced it.
[00:04:24] It means serialize de serialize and we also need a features right and the features will equal derive, that means we can use the derive macro on structs. So much like how our clap one had a derived macro. Which allowed it to be able to add a little proc macro at the top of our struct, we can do it again with this.
[00:04:46] Now on top of it, we need to add one more thing, serde is just a serialized deserialized library, so we need to add a subtype library for the serialization. Which means serde just allows you to create your own serialization if you want to, boom, we're gonna add serde.json.
[00:05:01] This will allow us to do all that fun serialization via JSON. So at the top of our struct, let's add a drive. We're gonna just add debug, just in case we need to print this out. I always usually end up adding debug. It doesn't really have much impact in your life, it's fantastic, we'll just do that here.
[00:05:21] And so, we'll do derive debug and then we'll do serialize deserialize. And so all this is gonna do is just gonna send a note, let's see, whoa we need to do a little LSP restart. Whenever you install a new library rust analyzer needs to restart, it's going to do a couple things or at least I find that it's better if you restart it.
[00:05:41] There we go we can import serde serialize and deserialize, now we have the power to serialize and deserialize this structure for you, which is pretty cool. There's a lot of things you can add on top of this, you can even use enums and say, hey, this enum is based off the tag and if the names match up, I'll leave in select the correct enum.
[00:06:00] So if you have like some sort of type on your heterogeneous list and JSON, it will do that for you. It's pretty cool, it's actually a really powerful library or not, we don't have anything very powerful we'll be doing, so we'll just keep it like this. All right, so next we need to create our struct projector, right?
[00:06:16] So this is the same thing, we're gonna wanna do the config, so config which is gonna be a config from our config great and of course we want our data, which is gonna be our data, which is straight above us, awesome. We have our projector, so let's implement a method that exists on the stet like on the static side of the struct if you're familiar with Java or even TypeScript like a static method on struct.
[00:06:43] So go like this info, actually, I forgot one thing. We should probably make this public, other people are gonna wanna see that. So let's have an info projector. And if we don't include self, the function is quote unquote static. I'm using the term correctly from TypeScript that way, it hopefully has a good one to one mapping.
[00:07:04] So we'll just say this, we're gonna go from config, right, let's take in a config and we can return self. Self of course being the projector object and so just like before we had to do all the same operations, now we can either do it this way, or we can use a trait.
[00:07:22] Now I always am a huge fan of using traits. I think it's a ton of fun to use traits. So we could also do a TryFrom, right, or just really we're only gonna be using FromConfig for projector, right, so this is another option we have. And so, I tend to just favor using traits as opposed to these type of methods just because I don't know, I just really liked that.
[00:07:45] For this time, we're actually gonna keep it here. Just to know, do it a little bit different, you can kind of experience something a bit different, and we'll do it from here. All right, so what are we gotta do here? We're gonna do the exact same set of operations that we've done before.
[00:07:58] With need to get everything, we need to get the config file, deserialize it, use it else return a default projector, right. All right, so from config will do the exact same check does this file exist already? If it does we can just do that. Now, I believe this is still a nightly feature, but there is this beautiful tri exists, right?
[00:08:21] This is fantastic. And so, if we were to do this, we can go config, config, but if I'm not mistaken, if it is okay, that means this thing is, it exists, awesome. But if I'm not mistaken, use of unstable feature, so we're not going to use that feature, for now we'll be using just the stable items.
[00:08:45] This is fantastic though, I would be very happy to use this. But just to make everything simple because I only told you guys to install stable but if you want you could use nightly, let's do something a little bit different here. Let's go, sdfs and then there is something I believe it's called metadata.
[00:09:01] Yeah, yeah, all right, we got it. And so, metadata is equivalent to the stat operation that you saw us do and go. So we'll do the exact same thing, config config and do an is okay. So if there is metadata, well, guess what? We're gonna use it right, awesome.
[00:09:18] We're all happy about that. So if there is metadata, let's read the file and then on top of that, let's then serialize it. So we're just going to do it in these nice little steps right here one at a time, okay? So let's go let contents equals standard fs, read the string, we're gonna pass in obviously, the file path config,config.
[00:09:45] And now we have this, we do have one problem of course, which is if contents isn't a risk or if it is a result object, well, we need to somehow unwrap this without throwing an error because we are doing a non error version of this, correct. That's what we didn't the other languages, we actually ignored all the other, here, we'll go to projector.
[00:10:04] If you notice in this one, when we did it, where are you from new projector, there you go. Yeah, we avoided all errors. We just want this thing to be an error free method, for this exact case. So we'll do the exact same thing here. So it does make it a little bit more tricky.
[00:10:19] There's a lot of operations we could do. We could figure out a way to do a map. I believe there isn't and then, we can kind of work this in and we could probably do something that's pretty good and functional, but for now we'll just kind of keep it procedural.
[00:10:31] Very good time to just definitely take some time and do some more of the functional stuff but for the sake of just learning, let's just keep it right here and go. If this thing is okay, we could unwrap this or we could just provide something a little bit smarter here.
[00:10:49] So what I'm gonna do is, I'm gonna do this. Let's go like this, let's contents equals contents, unwrap or so what the or is, is that I'm gonna unwrap this thing, but if there is an error, I'm just gonna provide a default value. So what is our default value?
[00:11:03] Well, we can make this pretty clever, right? I could go projector. All right, and give really just the proper JSON that we're kind of expecting to get. And so now, we're almost there. So now all we need to do is just simply, we can go data = serde_json from string.
[00:11:25] I believe that thing is the one that we want to use, and just use contents. And I believe at this point, it's gonna say, hey, use a borrow because of course from string it expects a reference to a string. Now this is obviously a much harder thing to read than you're probably used to but effectively all this is saying is that, this is the lifetime a that exists and it's gonna live as long as this and it's gonna return out to the objects you want, right.
[00:11:48] It's just defining how long these things can live. You don't really, we're not gonna go over lifetimes, but I'm gonna try to make something with it, we're just trying to keep things easy. But that's why it works and that's also reason why it's very efficient, is that I don't have to give it a copy of my string, I can just hand it a reference to my string and it can produce out for me the JSON object.
[00:12:09] All right, so there we go we have this beautiful data and if there is an error well then we need to do something if there is not an error If we don't need to do something. So let's go like this, we can do another unwrap or so let's go let data equals data unwrap or, so remember again, there was a serde issue.
[00:12:30] So we can just create a data right in line if we really want to. All right, here up being very go about it and let's go projector and we can go Hashmap new. We can also use like HashMap default up, we do default. Failed to resolve, what is the thing that we're doing?
[00:12:50] My goodness, that's why. Let's see, what is happening here? I didn't spell HashMap very well, so fantastic put it one of those here. We're still getting, of course, one error this entire time, which is kind of a annoying error but what it is is it's like, hey, you're you're not returning anything, please return something.
[00:13:07] The rest analyzer has no, cuz it's just always wanting to let you need to return something, maybe a little too in your face about it but this works out there we go. So we've kind of removed all of the potential problems, it's just the default data and so now we can just return a OK, open on OK, just a projector, with of course the config and of course the data, right?
[00:13:30] Beautiful, it's just absolutely beautiful but now all sudden we have this weird thing, where there's multiple errors going on here. You may not really realize that there's multiple errors going on here. The first arrow here is that, it's trying to still get us to return that. So we're not going to do that.
[00:13:46] So let's just let's just get over that really quickly. I'm going to just do this right here and move that down here and I'm gonna create one default. All right, now for this and we'll just yank this thing out right here. All right, cuz we're gonna use this a couple times.
[00:14:11] So let's just yank it out, awesome. There is something called default, it's actually a nice little trade. It might have taken the exact same amount of time, but we'll just go default data, awesome. So we have this beautiful default data up, cannot find, what did I call it?
[00:14:31] Default projector evidently wrong. There we go. And of course, data default_data, so let's get rid of that first error so we can kind of see what's happening here. So we got another error right here. What is this? I bet we can all guess from our earlier time in rust.
[00:14:48] It expected a string, but what did you hand it? You handed it a reference. So now we know, so we need to turn this thing into an actual string. Again, we'll just use the string from. We can use obviously into, to string we can use a lot of things.
[00:15:03] I'll just use the constructor, there we go. This is looking pretty good though, right? I feel like we've kind of got everything we need to have here. And so, awesome, there we go. That's all it's gonna take to correctly read from a file, prevent all errors, make sure it's defaulted, and it's beautiful.
[00:15:19] So, let's move on and start implementing the methods on projector.