Day 32 to 39 - Keeping the pace
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!