Skip to content

Commit

Permalink
wc blog
Browse files Browse the repository at this point in the history
  • Loading branch information
aadi-1024 committed Mar 25, 2024
1 parent 728d0e8 commit 3558041
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 0 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
source "https://rubygems.org"

gem "jekyll-theme-chirpy", "~> 6.5", ">= 6.5.5"
gem 'jekyll-compose', group: [:jekyll_plugins]

group :test do
gem "html-proofer", "~> 4.4"
Expand Down
114 changes: 114 additions & 0 deletions _posts/2024-03-25-gowc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
title: wc in Golang
date: 2024-03-25 11:05:00 +0530
categories: [Golang]
tags: [golang, linux, codingchallenges]
---

So I recently came across [codingchallenges.fyi](https://codingchallenges.fyi) through some Reddit post - if memory serves me right. As someone with the attention span of a skibidi-toilet kid, I found it to be perfect for me. Rather than being a full walkthrough about how to build something, the author merely provides a little overview, and the increments in which you should develop the project. The minimal handholding leaves it open for you to research on your own and approach the project as you wish without having to follow the author's design. So I decided to dabble into it today, and completed the first project - the `wc` unix command. If you have ever messed around with Linux you'll probably recognize it as one of the coreutils. Enough yapping done I guess, let's move on to the implementation.

> In case you just want the code, it's available [here](https://github.com/aadi-1024/gowc) on my github.
{: .prompt-info}

## Step 0
### Setting up the environment

I'm going to be using Golang for this project(duh), and also for all my future projects unless I can come over the Rust skill issue or sell my soul to Python. I'm using Go 1.22 (haha arch btw) but any version not built by Jesus himself would work. If you still want a newer version, you can use nix or brew(if you're on mac), or maybe Docker. Or maybe just get the latest version from the website, gah. Also if you're on Windows and not using wsl, just use wsl.

So, creating the project directory and initializing the go module.

```sh
~ ❭❭❭ cd Projects/
Projects ❭❭❭ mkdir gowc && cd gowc
gowc ❭❭❭ go mod init github.com/USERNAME/gowc
```

I use Goland but you may as well use VSCode, vim, helix or whatever fits you.

Create a `.gitignore`. I usually add .idea/ or .vscode/ to it. Download the [sample text](https://www.dropbox.com/scl/fi/d4zs6aoq6hr3oew2b6a9v/test.txt?rlkey=20c9d257pxd5emjjzd1gcbn03&dl=0) provided and place it in the root of your project. Add it to .gitignore if you wish.

## Step 1
### Outputting the number of bytes

So in the first iteration, all our tool needs to do is accept a flag (`-c` just like original wc) and output the number of bytes - easy right!

Here let's just get this running so you can get a hang of how things work, we'll refactor everything later.

First thing we need to do is, check the flags and only proceed if -c is provided.

### Flags - to framework or not?

Here you have the option to either use something like `cobra` or `ufave/cli` in order to handle the cli flags, or just use `flag` from the stdlib.

Although the framework approach might be suitable for larger tool, I'd suggest just using the stdlib for our dead simple tool.

So let's populate the `main.go` file. I usually place it in a directory named `cmd`. You do as you please, you can always reference the repo later on!

So starting off

```go
package main

import (
"flag"
"fmt"
"os"
"path/filepath"
)

const (
SUCCESS = 0
FAILURE = 1
)

func main() {
//filepath check
filePath := os.Args[len(os.Args)-1]
fd, err := os.Open(filePath)
if err != nil {
fmt.Println("Invalid filePath")
os.Exit(FAILURE)
}

//byte count
c := flag.Bool("c", false, "gowc -c filename")

flag.Parse()
var bytes int64

if *c {
fileinfo, err := fd.Stat()
if err != nil {
fmt.Println(err)
os.Exit(FAILURE)
}
bytes = fileinfo.Size()
}
fmt.Printf("%v %v\n", bytes, filepath.Base(filePath))
os.Exit(SUCCESS)
}

```
{: file="cmd/main.go"}

That sure's a lot of code! Breaking it down.

After importing our packages, we declare the exit codes. In the main function:
- First we need to check whether the file provided exists or not. If it doesn't, `os.Open` returns an error and we quit nice and simple.
- `c := flag.Bool("c", false, "gowc -c filename")` here I used the Bool function of the flag package. What does it do? Any half decent text editor with the appropriate plugins should show it nice and clear, but in case yours doesn't, you can always use [the docs](https://pkg.go.dev/flag). But anyways, as the docs describe, the Bool function takes 3 arguments and returns a pointer to a bool. The first argument is the flag, c in our case. The second is the default value, and third is the help message for the flag. The bool returned would be true if the user uses the flag, false otherwise.
> In case you are not comfortable reading documentation, spend some time and make sure you are. It's probably the best decision you'll make!
{: .prompt-tip}
- `flag.Parse()` parses the flags from os.Args. Color me surprised!
- *c dereferences the pointer and returns its value. Using the `fd` we got from `os.Open`, we get the file metadata using `fd.Stat`, check for errors and then get the file content size(in bytes), print it and exit nice and sweet. Easy!

And that is it! Our app is complete(not really!!)!

Now to test it
```sh
gowc ❭❭❭ go run cmd/*.go -c test.txt
342190 test.txt
```
It Works!!

## Step 2
##### coming soon!

0 comments on commit 3558041

Please sign in to comment.