custom header About: Photo by Maik Jonietz on Unsplash

Quick note about the duration of this challenge

Back on October 7th, I made it to the technically 92th day of this challenge if I would have done at least an hour of coding each day, but it is not as easy when you have a full-time job, other studies ongoing (as for myself, I’m also currently studying catalonian) and other obligations.

I’ve already accepted that I won’t be able to finish this challenge by the expected date goal (November 19th), yet I believe that making this learning process part of my routine until the end of the year or so, updating this blog with my progress and my will to learn should be fine as well, learning is a process, a marathon of sorts, not a speedrun.

I don’t want to make excuses, yet it is true that I feel how life has gotten in between, making me a bit of lack in practice and context for what I came to learn upon this point.

That being said, let’s get back to it.

Day 32 - 25/10/2024

Today I kept it easy on myself and got back to the book I was reading, I finished another chapter! This one was about error handling and logging. Now I am on the verge of getting introduced to how to use MySQL databases and all, also almost finished the first quarter of the book thus far!, so I think that stopping here and focusing onto the CRUD part of my own project would be interesting to know, so when I learn about databases I can actually apply it on my own project of sorts.

Day 33 - 26/10/2024

Got back to code, in short, I introduced the capability for my password manager to ask multiple times to introduce values onto the CSV file, this enables the user to not need to execute the script multiple times until all records are introduced.

As seen at main.go file:

for {
		// Write single line to file
		change.WriteLine(path)

		fmt.Println("=== Updated content of your file - Insert ===")
		fmt.Printf("\n")
		read.ReadFile(path)

		fmt.Println("\n Do you want to enter more data?[y/n]")

		var respData string
		fmt.Scan(&respData)

		if respData != "y" && respData != "Y" {
			break
		}
		fmt.Print("\033[;1H\033[2J")
	}

And the improved called function from write.go file:

func WriteLine(file string) {
	// Ask for values to be added to the file
	fmt.Println("Introduce your username")

	var dataWrite [3]string
	fmt.Scan(&dataWrite[0])

	fmt.Println("Now, introduce the site or URL for your user (i.e: Google / 'www.google.com')")
	fmt.Scan(&dataWrite[1])

	fmt.Println("Finally, introduce your password")
	fmt.Scan(&dataWrite[2])

	// Open the CSV file for appending
	fileW, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm)

	// Create a CSV writer
	writer := csv.NewWriter(fileW)

	// Write a new row to the CSV file
	// Current placed credentials were randomly generated and don't represent a real user
	// Expected structure for the CSV is [user,account/site, password]
	row := []string{dataWrite[0], dataWrite[1], dataWrite[2]}
	err = writer.Write(row)
	check(err)
	writer.Flush()
}

Day 34 - 27/10/2024

Here in Spain we have something totally unnecesary that is the time change twice a year, this particular one was to move from summer to winter time, or at least that’s the common justification, that made me mostly become really dizzy and lazy for the day, BUT, I achieved some minor progress over my password manager.

Now I have a new file totally dedicated to update entries already existent within the provided CSV file, as for now it requires to enter all three parameters in order to verify it exists, but I would like to explore a different approach in the future, if necessary.

package change

import "fmt"

func UpdateLine(file string) {
	// Ask for values to check
	fmt.Println("Update mode - Introduce your username")

	var dataUpdate [3]string
	fmt.Scan(&dataUpdate[0])

	fmt.Println("Now, introduce the site or URL for your user (i.e: Google / 'www.google.com')")
	fmt.Scan(&dataUpdate[1])

	fmt.Println("Finally, introduce your password")
	fmt.Scan(&dataUpdate[2])

	// Validate if field exists

	fmt.Println("Which field do you want to change?", dataUpdate[0], dataUpdate[1], dataUpdate[2])

}

I made it part of the change package, so I might be able to re-utilize the check function for error verification

func check(err error) error {
	if err != nil {
		return err
	}
	return nil
}

Day 35 - 28/10/2024

Keeping it up with the update process, I achieved to create a separate case for verification, that is in charge of modifying the array’s value into a new one also passed by the user as an input.

current status:

package change

import "fmt"

func switchValidate(v int, old *[3]string, new string) string {
	switch v {
	case 0:
		old[0] = new
		fmt.Printf("New value is: %v", new)
	case 1:
		old[1] = new
		fmt.Printf("New value is: %v", new)
	case 2:
		old[2] = new
		fmt.Printf("New value is: %v", new)
	default:
		fmt.Println("The number is wrong!")
	}
	return "none"
}
func UpdateLine(file string) {
	// Ask for values to check
	fmt.Println("Update mode - Introduce your username")

	var dataUpdate [3]string
	fmt.Scan(&dataUpdate[0])

	fmt.Println("Now, introduce the site or URL for your user (i.e: Google / 'www.google.com')")
	fmt.Scan(&dataUpdate[1])

	fmt.Println("Finally, introduce your password")
	fmt.Scan(&dataUpdate[2])

	// Validate/Verify if field exists
	fmt.Println("Which field do you want to change [0/1/2]?", dataUpdate[0], dataUpdate[1], dataUpdate[2])
	var updatePrompt int
	fmt.Scan(&updatePrompt)

	fmt.Println("Which value would you like to assign to that field? ")
	var newPrompt string
	fmt.Scan(&newPrompt)

	//Pass value alongside the content of the array
	switchValidate(updatePrompt, &dataUpdate, newPrompt)
}

Still pending to actually modify the value within the file, which I believe that needs to be done in a sql-like approach.

update (column/field) where register = “field1”,”field2”,”field3”

Day 36 & Day 37 - 29/10/2024 && 30/10/2024

I have had some issues while trying to figure out how to validate the content of the CSV file, so I reached out the community forums/discord in order to ask for help/advice on improvement. This conversations usually take a while to be answered so I went ahead of myself and made some minor fixes:

update.go:

func switchValidate(v int, old *[3]string, new string) string {
	var index int
	switch v {
	case 0:
		old[0] = new
		index = 0
		fmt.Printf("New value is: %v", new)
	case 1:
		old[1] = new
		index = 1
		fmt.Printf("New value is: %v", new)
	case 2:
		old[2] = new
		index = 2
		fmt.Printf("New value is: %v", new)
	default:
		fmt.Println("The number is wrong!")
	}
	return old[index]
}

Previously, the return pointed to a “none” plain string, as of now, it should return the new value of the selected field.

The code kept becoming more and more complex and had to rollback a couple of times, so I have no conclusion for this yet. For reference, I turned down the approach of using an array to iterate over the CSV, in favor of using a struct.

The following code won’t work


type CsvLine struct {
	Column1 string
	Column2 string
	Column3 string
}

[...]

// Loop through lines & turn into struct
	for _, line := range lines {
		data := CsvLine{
			Column1: line[0],
			Column2: line[1],
			Column3: line[2],
		}
		if data.Column1 == dataUpdate[updatePrompt] {
			fmt.Println("it worked", data.Column1)
		}
	}

Day 38 - 31/10/2024

Got it! now the update mode of my password manager works just fine. But, to be entirely honest, it was not just me, but the actual help of the community and a bit of chatgpt to sort a part of the code out, since I managed to actually change the value of the record and iterate over the file until the matching entry was found, but I had no clue on how to actually change the file’s content, since CSV files don’t support direct record updates.

This is the result for the update.go file

package change

import (
	"encoding/csv"
	"fmt"
	"os"
)

type CsvLine struct {
	Column1 string
	Column2 string
	Column3 string
}

var NewValue string

func switchValidate(v int, old *[3]string, new string) {
	switch v {
	case 0:
		old[0] = new
	case 1:
		old[1] = new
	case 2:
		old[2] = new
	default:
		fmt.Println("Invalid field number!")
	}
	NewValue = new // Set the new value globally
}

func UpdateLine(file string) {
	fmt.Print("\033[;1H\033[2J")
	// Get user inputs to identify the record
	var dataUpdate [3]string
	fmt.Println("Update mode - Introduce your username")
	fmt.Scan(&dataUpdate[0])

	fmt.Println("Now, introduce the site or URL for your user (i.e: Google / 'www.google.com')")
	fmt.Scan(&dataUpdate[1])

	fmt.Println("Finally, introduce your password")
	fmt.Scan(&dataUpdate[2])

	// Get the field to update and new value
	var updatePrompt int
	fmt.Println("Which field do you want to change [0 for username, 1 for site, 2 for password]?")
	fmt.Scan(&updatePrompt)

	var newPrompt string
	fmt.Println("Which value would you like to assign to that field?")
	fmt.Scan(&newPrompt)

	// Save a copy of the original data before modifying it
	originalData := dataUpdate

	// Update the selected field in `dataUpdate`
	switchValidate(updatePrompt, &dataUpdate, newPrompt)

	// Read the CSV content
	lines, err := ReadCsv(file)
	if err != nil {
		fmt.Println("Error reading CSV:", err)
		return
	}

	// Track if update happened
	updated := false

	// Iterate through lines and find the matching line
	for i, line := range lines {
		// Check if this line matches the original data criteria
		if line[0] == originalData[0] && line[1] == originalData[1] && line[2] == originalData[2] {
			lines[i][updatePrompt] = NewValue // Apply the new value
			updated = true
			break
		}
	}

	if updated {
		err = WriteCsv(file, lines)
		if err != nil {
			fmt.Println("Error writing CSV:", err)
		} else {
			fmt.Println("Record updated successfully.")
		}
	} else {
		fmt.Println("No matching record found.")
	}
}

// ReadCsv reads the CSV file into a 2D slice
func ReadCsv(filename string) ([][]string, error) {
	f, err := os.Open(filename)
	if err != nil {
		return [][]string{}, err
	}
	defer f.Close()

	lines, err := csv.NewReader(f).ReadAll()
	if err != nil {
		return [][]string{}, err
	}

	return lines, nil
}

// WriteCsv writes the CSV data back to the file
func WriteCsv(filename string, data [][]string) error {
	f, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	writer := csv.NewWriter(f)
	defer writer.Flush()

	return writer.WriteAll(data)
}

Day 39 - 03/11/2024

Honestly, this Halloween weekend has been a bit of a convoluted one, I tried to keep on doing code yet it was not that much of a possible thing due to a time and schedule restriction, so now I am officialy out of possibilities to finish this challenge within 2024 as a whole.

Now, moving on, today I just updated the README.md file for my Password manager project and moved arround a bit to make sure I understood everything from my own code.

Now, this entry is long enough, and I’ve reached another goal, getting to the 40th day!

Izma iconic pull the lever kronk scene