Hey guys, welcome back to the DriveSinker project. I just opened up this project right now, and we're setting things up. There is so much we need to do. Let's open up the .gitignore file over here. No long intros over here. We're just going to go straight into the code.
I want to create a function over here. This will be a pointer receiver. And where's my IntelliSense? I just opened up this project right now. It's still loading. Okay, there we go. I'll call this `shouldIgnore`, and we basically just want a function that tells us whether to ignore a certain file or not, right? This will take the path, which will be a string, and the dir entry, which will be in the `fs` package, and the dir entry struct in here. I'm not going to return anything for now. Let's just implement the function first.
So the first thing I want to do is to grab the relative path that we're getting in this function over here, right? So this is going to be an absolute path. I want to convert this into a relative path, so we can use the `filepath` package to do that. For the base path, we'll pass in the... I should probably rename this to just a `g` over here. So let's type in `g`, and the base path will just be the folder path. And over here, we'll pass in the path, of course. Handle any errors as well. If we can't convert the relative path, then let's just say that the relative path is the path that we get in the function arguments.
Now let's actually clean this up a bit as well by using `filepath.ToSlash` and then pass in the path over here. And what this is basically going to do is it's going to replace the separator characters with a slash character. So you know that paths in Windows, for instance, have a... Let me show you. And oh my God, I totally forgot to zoom again. There we go. You know the paths in Windows have this backslash symbol, right? So it's like `C:\Users\Hassan` or something like that, right? Whereas in Linux or in other operating systems, we use a regular slash character, right? So this function is really just here to normalize the path, right? Just to clean it up and make sure that we're not using any invalid characters or anything.
So with that done, now we can actually start to loop over the entire patterns list. What's it called? `g.toIgnore`. And we can actually start to test every single pattern and see which ones match on this path, right? So I'm going to create some initial variables. We'll say `ignored`, which is a Boolean. This will be false by default, and also `target`, which will be a string.
Now let's see if `p.fromRoot`, then we'll say `target` equals the relative path. Otherwise, we say `target` equals the base folder name of the relative path. Now, what exactly am I doing over here? We discussed in the previous video about Git ignore parsing rules, that if the path starts from the root, then we're supposed to match that pattern from the root as well, right? Which is why I pass in the entire relative path in the target over here. Otherwise, we're supposed to match the pattern anywhere in the project at any folder depth, right? Which is why I only use the base name in this case, right?
Now let's create another variable called `match`. You might think, why am I using match when I already have a variable called `ignored` over here? Basically, match is going to tell us whether the pattern matched or not, and ignored is going to tell us whether we're going to ignore that pattern or not, right? And we need both of them because we also support negations and Git ignore, right? You'll see a lot more about this when we actually finish this function.
Now we also need to handle the double-star modes that we created in the last video, I guess. Let's say `case leading double stars`. If `strings.HasSuffix`, pass in the pattern and then just a slash and an asterisk, a wildcard character after that. This means that this pattern is a directory, and we're supposed to ignore everything inside this directory, right? So let's do that first of all. Let's just clean up the suffix. Let's say `TrimSuffix`, `p.pattern`, and pass in the exact same suffix over here. Then let's say `match, err := filepath.Match`. Pass in the pattern and target over here. Let's handle the error as well, and then let's just print the error over here. Let's say, "Invalid pattern," print the pattern as well as the error and a new line. There we go, there we go.
And out here, let's say else if the string doesn't have that suffix, right? Then our job is a lot simpler. We can just say `match` equals `strings.HasSuffix`, `p.pattern`, relative path, and then let's say a slash over here plus `p.pattern` over here. Or let's add another conditional: the relative path equals `p.pattern` over here. There we go. And also, you see an error over here. Let's just remove the colon symbol over here, so we are assigning the match value to this match variable up here and not creating a new one.
Okay, now let's move on to the next case. Let's say trailing double stars. This will be very similar to this line over here. We'll say if the relative path equals `p.pattern` or `strings.HasPrefix`, relative path, slash, plus... Actually, not plus. Over here, we concatenate the pattern. Remember, this is trailing double stars, not leading, right? This comes at the beginning of the pattern, not the end. So the slash in this case will go to the end, and we use `HasPrefix` instead of `HasSuffix`.
Okay, that's all we need to do.
If either of these conditions match, then we just say match equals true. And then we move on to the final case, which is middle double stars. Now, this one has a pattern that looks something like this, right? You have some folder name, and you have double stars between there. And then you have another folder name and some path after that, right?
So we need to first of all split that into two parts. So for that, we can use strings.SplitN. We'll pass in P.pattern, a slash, and let's say two over here. Then let's say if the length of parts equals two, then we'll say the left and right parts are parts zero and parts one.
And we'll say match equals strings.Contains(relativePath, left), and let's also separate this into new lines as well. strings.Contains(relativePath, right), so it contains both of them. And let's make sure that strings.Index(relativePath, left) is greater than, or actually less than, strings.LastIndex(relativePath, right).
Basically, what I'm saying in this conditional over here is that the left folder name comes before the right folder name. It's an extra check over here, right? And that's all we need to do over here.
Now we can actually add a default case over here. We'll say match and error equals filepath.Match. Again, notice that I'm not using a colon over here, right? I'm not trying to declare a new variable. I'm just reassigning the variables or reassigning the values to the existing variables.
So pass in the pattern over here, as well as the target. Handle the error as well. Let's say log.Printf, invalid pattern, print the pattern and the error, and then a new line. There we go. And if there is an error over here, let's also continue on to the next pattern, the next pattern, so we don't waste any time trying to parse and match with invalid patterns.
Now out here, if the pattern was matched, let's check the negation of the pattern. If we are supposed to negate the pattern, we'll say ignored equals false. Otherwise, if this is just a regular pattern without any negations, we'll say ignored equals true, right?
If ignored, then we need to return the value over here, right? We need to return some sort of a Boolean field that tells us whether we should ignore this or not.
Now, if you go back to sync, which is broken right now until we finish the new tracker, but if we go down here, you see that right in this block where we actually ignore files and folders, if the path is a directory, then we use the filepath.SkipDir error over here, right? Otherwise, we just return a nil over here.
And this filepath.SkipDir allows us to skip entire directories without having to walk through every single file in that directory. And that is extremely useful, right? But there's literally no way to represent that in just a Boolean.
In a Boolean, you only have true or false, right? That's all that this function can return, right? If we're supposed to allow the file, we can say true. Otherwise, we can say false. But what about if the file is a directory and we want to ignore that, right?
So we need a third return value. And the easiest way to implement this is to just go up here and create an enum. So that's what I'm going to do. We'll say type IgnoreReturnType, and I'll call this an integer. And then let's declare some constant values for this.
We'll say IgnoreIgnoreDir, pass in the type as well, and call this iota. There we go. IgnoreFile and Allow. So that's the three things we need to have right now.
Down here, at the end of this function, let's say log.Printf. It's always a good idea to have logs, guys, and print the path over here. Let's say if d.IsDir, then we return IgnoreDir. Otherwise, we return IgnoreFile.
If ignored is not true, then none of this will run, and we're supposed to allow the file. So in that case, we'll just return Allow by default, right? And I should probably do that out here at the very end of the function.
And at the top of this function, let's also add the return type over here so we don't have any errors. And with that, we are completely done with the ShouldIgnore function. I'm going to stop the video over there, and in the next video, we're going to integrate this entire thing with our synchronizing algorithm over here. And then we'll hopefully finally be done with this and have a new custom gitignore implementation that really just ignores all of the complex patterns that we put in our gitignore profile.
Stay tuned for that. Like, comment, subscribe, and do all those wonderful things for me.
Real quick, guys, while I still have your attention over here, let me do a quick self-promotion plug. This is my professional website, and I work as a freelancer to develop websites and mobile apps and all those wonderful things for people.
If this is something you're interested in, or you know someone who's interested in this and you want to work with me to develop some sort of a project or app or whatever, right, you can book a free meeting with me over here. Or you can use the top link in the description to book a call with me. It's completely free, and you can talk about your project and start building it right now.
So if that's something you're interested in, check out the top link in the description. Other than that, thank you for watching the video, and I'll see you in the next one.