diff options
| author | jet2tlf <jet2tlf@gmail.com> | 2024-06-03 17:36:39 +0000 |
|---|---|---|
| committer | jet2tlf <jet2tlf@gmail.com> | 2024-06-03 17:36:39 +0000 |
| commit | 7cff8936edbdc7ce7c0ca41eab2f8d5470cad2e6 (patch) | |
| tree | 931ab66c1d6be7adc37fbe810687e00297d5911f | |
| parent | a312577297526cfd3a51f68d8c112229f3c4185a (diff) | |
| download | bittorrent-go-7cff8936edbdc7ce7c0ca41eab2f8d5470cad2e6.tar.gz bittorrent-go-7cff8936edbdc7ce7c0ca41eab2f8d5470cad2e6.zip | |
codecrafters submit [skip ci]
| -rw-r--r-- | cmd/mybittorrent/file.go | 85 | ||||
| -rw-r--r-- | cmd/mybittorrent/main.go | 51 |
2 files changed, 105 insertions, 31 deletions
diff --git a/cmd/mybittorrent/file.go b/cmd/mybittorrent/file.go new file mode 100644 index 0000000..2302976 --- /dev/null +++ b/cmd/mybittorrent/file.go @@ -0,0 +1,85 @@ +package main + +import ( + "bytes" + "fmt" + "io" +) + +type File struct { + Announce string `bencode:"announce"` + Info FileInfo `bencode:"info"` +} + +type FileInfo struct { + Length int `bencode:"length"` + Name string `bencode:"name"` + PieceLength int `bencode:"piece length"` + Pieces string `bencode:"pieces"` +} + +func NewFile() *File { + return &File{} +} + +func (f *File) ReadFrom(r io.ReadCloser) error { + buf := new(bytes.Buffer) + + _, err := buf.ReadFrom(r) + if err != nil { + return err + } + + defer func(r io.ReadCloser) { + err := r.Close() + if err != nil { + fmt.Printf("failed to close: %v+\n", err) + } + }(r) + + if err != nil { + return err + } + + decoded, err := NewDecoder(buf.String()).Decode() + if err != nil { + return err + } + + content, ok := decoded.(map[string]any) + if !ok { + return fmt.Errorf("invalid contents") + } + + f.Announce, ok = content["announce"].(string) + if !ok { + return fmt.Errorf("invalid announce field") + } + + info, ok := content["info"].(map[string]any) + if !ok { + return fmt.Errorf("invalid info field") + } + + f.Info.Length, ok = info["length"].(int) + if !ok { + return fmt.Errorf("invalid info.length field") + } + + f.Info.Name, ok = info["name"].(string) + if !ok { + return fmt.Errorf("invalid info.name field") + } + + f.Info.PieceLength, ok = info["piece length"].(int) + if !ok { + return fmt.Errorf("invalid info.piece length field") + } + + f.Info.Pieces, ok = info["pieces"].(string) + if !ok { + return fmt.Errorf("invalid info.pieces field") + } + + return nil +} diff --git a/cmd/mybittorrent/main.go b/cmd/mybittorrent/main.go index e3e902a..7905e01 100644 --- a/cmd/mybittorrent/main.go +++ b/cmd/mybittorrent/main.go @@ -4,42 +4,14 @@ import ( "encoding/json" "fmt" "os" - "strconv" - "strings" - "unicode" // bencode "github.com/jackpal/bencode-go" // Available if you need it! ) -func decodeBencode(bencodedString string) (interface{}, error) { - if unicode.IsDigit(rune(bencodedString[0])) { - var firstColonIndex int - - for i := 0; i < len(bencodedString); i++ { - if bencodedString[i] == ':' { - firstColonIndex = i - break - } - } - - lengthStr := bencodedString[:firstColonIndex] - - length, err := strconv.Atoi(lengthStr) - if err != nil { - return "", err - } - - return bencodedString[firstColonIndex+1 : firstColonIndex+1+length], nil - } else if rune(bencodedString[0]) == 'i' { - return strconv.Atoi(bencodedString[1:strings.IndexByte(bencodedString, 'e')]) - } else { - return "", fmt.Errorf("only strings are supported at the moment") - } -} - func main() { command := os.Args[1] - if command == "decode" { + switch command { + case "decode": bencodedValue := os.Args[2] decoded, err := NewDecoder(bencodedValue).Decode() @@ -50,7 +22,24 @@ func main() { jsonOutput, _ := json.Marshal(decoded) fmt.Println(string(jsonOutput)) - } else { + + case "info": + f, err := os.Open(os.Args[2]) + if err != nil { + fmt.Println("Failed to open file: " + os.Args[2]) + } + + tf := NewFile() + + err = tf.ReadFrom(f) + if err != nil { + fmt.Println("Failed to parse file: " + os.Args[2]) + panic(err) + } + + fmt.Printf("Tracker URL: %s\nLength: %d", tf.Announce, tf.Info.Length) + + default: fmt.Println("Unknown command: " + command) os.Exit(1) } |