package main

import (
	"errors"
	"fmt"
	"os"
)

// Produk tidak memiliki method String,
// sehingga fmt memakai format bawaan untuk struct.
type Produk struct {
	Nama  string
	Harga int
}

// Siswa mengimplementasikan interface fmt.Stringer.
type Siswa struct {
	Nama string
	Umur int
}

// String membuat fmt memakai teks ini alih-alih format struct bawaan.
func (s Siswa) String() string {
	return fmt.Sprintf("siswa %s (%d tahun)", s.Nama, s.Umur)
}

func main() {
	// Keluarga Sprint menghasilkan string tanpa mencetak apa pun.
	sapaan := fmt.Sprint("Harga kopi: ", 25000)
	barisan := fmt.Sprintln("Stok kopi:", 12)
	ringkas := fmt.Sprintf("%s punya %d gram", "Budi", 250)
	fmt.Println(sapaan)
	fmt.Printf("sprintln: %q\n", barisan)
	fmt.Println(ringkas)

	// Verb untuk struct tanpa method String.
	kopi := Produk{Nama: "Kopi Gayo", Harga: 25000}
	fmt.Printf("%%v  : %v\n", kopi)
	fmt.Printf("%%+v : %+v\n", kopi)
	fmt.Printf("%%#v : %#v\n", kopi)
	fmt.Printf("%%T  : %T\n", kopi)

	// Verb untuk nilai dasar.
	fmt.Printf("%%d  : %d  %%x : %x\n", 255, 255)
	fmt.Printf("%%s  : %s  %%q : %q\n", "Go", "Go")
	fmt.Printf("%%x  : %x (string)\n", "Go")
	fmt.Printf("%%f  : %f\n", 3.14)
	fmt.Printf("%%g  : %g  %%e : %e\n", 3.14, 3.14)
	fmt.Printf("%%t  : %t\n", 255 > 100)

	// Lebar dan presisi kolom.
	fmt.Printf("[%%8.2f] : [%8.2f]\n", 1234.567)
	fmt.Printf("[%%-10s] : [%-10s]\n", "Go")
	fmt.Printf("[%%05d]  : [%05d]\n", 42)

	// Fprintf menulis ke io.Writer mana pun, bukan hanya layar.
	fmt.Fprintf(os.Stdout, "fprintf ke os.Stdout: %s\n", "berhasil")

	// Stringer dipanggil secara otomatis oleh fmt.
	siti := Siswa{Nama: "Siti", Umur: 21}
	fmt.Println(siti)
	fmt.Printf("%v dan %s\n", siti, siti)

	// Verb %w membungkus error agar tetap bisa diperiksa dengan errors.Is.
	galat := errors.New("sambungan database gagal")
	dibungkus := fmt.Errorf("ambil data: %w", galat)
	fmt.Println(dibungkus)
	fmt.Println("errors.Is:", errors.Is(dibungkus, galat))
}
