this post was submitted on 09 Dec 2023
8 points (100.0% liked)

Golang

2217 readers
1 users here now

This is a community dedicated to the go programming language.

Useful Links:

Rules:

founded 1 year ago
MODERATORS
 

Hi 👋

I have tried to learn some go but I am still very much at the beginning, like understanding how to work with variables and functions. My background is mostly in python but I am not a programmer by trade.

package main

import (
	"crypto/sha256"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
)

func write_lines_to_file(lines []string, output_file string) {
	f, err2 := os.Create(output_file)
	if err2 != nil {
		log.Fatal(err2)
	}
	defer f.Close()
	for _, line := range lines {
		_, err := f.WriteString(line + "\n")
		if err != nil {
			log.Fatal(err2)
		}
	}
}

func get_size_and_hash(file_path string) (int, string) {
	file, err := os.Open(file_path)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	hash := sha256.New()
	if _, err := io.Copy(hash, file); err != nil {
		panic(err)
	}
	sum := fmt.Sprintf("%x", hash.Sum(nil))
	file, err2 := os.Open(file_path)
	if err2 != nil {
		log.Fatal(err2)
	}
	fi, err2 := file.Stat()
	if err != nil {
		log.Fatal(err2)
	}
	my_size := fi.Size()
	return int(my_size), string(sum)
}

func get_list_of_files(target_directory string) []string {
	var files []string
	err := filepath.Walk(target_directory, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			fmt.Println(err)
			return nil
		}
		if !info.IsDir() {
			files = append(files, path)
		}
		return nil
	})
	if err != nil {
		log.Fatal(err)
	}
	return files
}

func main() {
	// accept directory as user input
	target_directory := os.Args[1]

	my_files := get_list_of_files(target_directory)

	var content []string

	for _, file := range my_files {
		size, hash := get_size_and_hash(file)
		var str_file string = string(file)
		str_size := fmt.Sprint(size)
		var str_hash string = string(hash)
		// structure: file, checksum, size
		combined_line := str_file + "," + str_hash + "," + str_size
		content = append(content, combined_line)
	}

	var output_file string = target_directory + "/PULP_MANIFEST"
	write_lines_to_file(content, output_file)
}

I am testing this using the following command: rm -f test_input/PULP_MANIFEST && go fmt pulp_manifest.go && go build pulp_manifest.go && ./pulp_manifest test_input && cat test_input/PULP_MANIFEST on Fedora with go 1.20

Known Limitations

  • My rewrite does not handle files or directories with "," yet.

Untested

  • Files with binary content
  • Paths on macOS or Microsoft Windows
  • Paths with whitespace
  • Symlinks in target_directory
  • target_directory as symlink

I am looking for the following feedback:

  • bugs and limitations
  • a was to add tests: do you have any recommendations for talks or blog posts?
  • style & best practice
  • a way to use static typing?!
  • anything else that you would recommend a novice.

Right now, I believe my rewrite works. Feel free to shatter my assumption. Cheers.

top 1 comments
sorted by: hot top controversial new old
[–] icb4dc0de 6 points 11 months ago* (last edited 11 months ago)

Haven’t copied it yet to an editor so just a few things:

  • naming convention in Go is camelCase not snake_case
  • prefer to accept io.Reader over opening files in every function. Improves testability but also is best practice
  • return errors whenever possible instead of just logging them or do something about if you can (like creating a file if it doesn’t exist yet if it makes sense) - don’t use panic if you don’t have to! Panics are only a last resort if there’s no way to handle the error gracefully, think: compile a regex as a global variable that you need and that’s static, the expression won’t change magically so there’s no way the program can continue without a valid expression
  • move logic to some package (== sub-directory with a package name != main) and have at least one function with a PascalCase name to call from you main package. Functions starting with a capital letter are public whereas functions starting with a lowercase letter are private - not by convention but enforced by the compiler!

For a more thorough review I’ve to get to a computer but probably someone else has some more tips for you :)

Also check out the docs for testing https://pkg.go.dev/testing should hopefully get you started