diff options
| author | Anhgelus Morhtuuzh <william@herges.fr> | 2026-03-15 14:01:24 +0100 |
|---|---|---|
| committer | Anhgelus Morhtuuzh <william@herges.fr> | 2026-03-15 14:01:24 +0100 |
| commit | 1ee4d17b20480b2d34ef289deab57fef77729b58 (patch) | |
| tree | 4d1ccf0262c81548efbe3c86fb9cdb84693e2f6a /human.go | |
| parent | 16b8e2eee103def961ee6871d7e9ae2626b99475 (diff) | |
feat(human): json operations
Diffstat (limited to 'human.go')
| -rw-r--r-- | human.go | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/human.go b/human.go new file mode 100644 index 0000000..d78d31f --- /dev/null +++ b/human.go @@ -0,0 +1,96 @@ +package human + +import ( + "encoding/json" + "errors" + "net/url" + "strings" + "time" +) + +const Version = "0.1.1" + +var ( + stdPorts = map[string]string{ + "http": "80", "https": "443", "gemini": "1965", + } +) + +var ( + ErrInvalidURL = errors.New("invalid url") +) + +// URL represents a standard [url.URL] following normalization rules of human.json. +type URL url.URL + +func (u *URL) normalize() { + if strings.Contains(u.Host, ":") { + sp := strings.Split(u.Host, ":") + if v, ok := stdPorts[u.Scheme]; ok && v == sp[1] { + u.Host = sp[0] + } + } + u.Path = strings.TrimSuffix(u.Path, "/") +} + +func (u *URL) UnmarshalJSON(b []byte) error { + var s string + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + raw, err := url.Parse(s) + if err != nil { + return err + } + *u = URL(*raw) + u.normalize() + return nil +} + +func (u *URL) MarshalJSON() ([]byte, error) { + u.normalize() + return json.Marshal(u.String()) +} + +func (u *URL) String() string { + ur := url.URL(*u) + return ur.String() +} + +// Vouch a website to trust it. +type Vouch struct { + URL *URL `json:"url"` + VouchedAt time.Time `json:"-"` +} + +func (v *Vouch) UnmarshalJSON(b []byte) error { + type t Vouch + var r struct { + t + VouchedAt string `json:"vouched_at"` + } + err := json.Unmarshal(b, &r) + if err != nil { + return err + } + *v = Vouch(r.t) + v.VouchedAt, err = time.Parse(time.DateOnly, r.VouchedAt) + return err +} + +func (v *Vouch) MarshalJSON() ([]byte, error) { + type t Vouch + r := struct { + t + VouchedAt string `json:"vouched_at"` + }{t(*v), v.VouchedAt.Format(time.DateOnly)} + return json.Marshal(r) +} + +// Human represents the human.json file. +type Human struct { + Version string `json:"string"` + URL *URL `json:"url"` + Vouches []*Vouch `json:"vouches"` +} |
