document renames and simplification
authornsz <nsz@port70.net>
Tue, 15 Nov 2011 22:13:20 +0000 (23:13 +0100)
committernsz <nsz@port70.net>
Tue, 15 Nov 2011 22:13:20 +0000 (23:13 +0100)
document/document.go
document/document_test.go

index c550619..1c61fcb 100644 (file)
@@ -31,7 +31,7 @@ import (
 
 const ClearSignedHeader = "-----BEGIN PGP SIGNED MESSAGE-----\n"
 
-// (non-standard) MIME subtype for epoint documents, see RFC 2045 and RFC 2046
+// (non-standard) MIME type for epoint documents, see RFC 2045 and RFC 2046
 var ContentType = map[string]string{
        "cert":  "text/plain.epoint.cert; charset=utf-8",
        "draft": "text/plain.epoint.draft; charset=utf-8",
@@ -53,17 +53,17 @@ type Field struct {
 
 // Draft document represents an obligation transfer order
 type Draft struct {
-       Drawer       string `marshal:"id"` // ID of the payer (signer of the document)
-       Beneficiary  string `marshal:"id"` // ID of the payee
-       Amount       int64  // amount transfered
+       Drawer       string `marshal:"id"`
+       Beneficiary  string `marshal:"id"`
+       Amount       int64
        Denomination string
-       IssueDate    int64 `marshal:"date" key:"Issue-Date"`
-       // Draft is bounced before this date
+       Issuer       string `marshal:"id"`
+       AuthorizedBy string `marshal:"id" key:"Authorized-By"`
+       Date         int64  `marshal:"date"`
        MaturityDate int64  `marshal:"date" key:"Maturity-Date"`
-       Notes        string // Arbitrary text notes of the drawer
-       Nonce        string // unique number
-       Server       string `marshal:"id"` // ID of the server
-       Drawee       string `marshal:"id"` // ID of the obligation issuer
+       ExpiryDate   int64  `marshal:"date" key:"Expiry-Date"`
+       Notes        string
+       Nonce        string
        // useful if more strict date of issue information is needed
        //References []string
 }
@@ -72,36 +72,35 @@ type Draft struct {
 // References previous certificate (if any)
 // and the transfer related other documents
 type Cert struct {
-       Holder           string `marshal:"id"` // ID of the creditor
-       Serial           uint32 // serial number, number of certs of the holder
-       Date             int64  `marshal:"date"` // date of issue
-       Balance          int64  // current obligation value
+       Holder           string `marshal:"id"`
+       Serial           int64
+       Date             int64 `marshal:"date"`
+       Balance          int64
        Denomination     string
-       Server           string   `marshal:"id"`                 // ID of the server
-       Issuer           string   `marshal:"id"`                 // ID of the obligation issuer (drawee?)
-       LastDebitSerial  uint32   `key:"Last-Debit-Serial"`      // serial of the last draft cert or 0
-       LastCreditSerial uint32   `key:"Last-Credit-Serial"`     // serial of the last credit cert or 0
-       LastCert         string   `marshal:"id" key:"Last-Cert"` // ID of the previous cert if any
-       Difference       int64    // difference from previous balance
-       Draft            string   `marshal:"id"`                   // draft ID related to the transfer
-       Drawer           string   `marshal:"id"`                   // ID of the drawer in the transaction
-       DrawerSerial     uint32   `key:"Drawer-Serial"`            // serial of the drawer's related debit cert
-       DrawerCert       string   `marshal:"id" key:"Drawer-Cert"` // ID of the drawer's related debit cert
-       Notes            string   // Arbitrary text notes of the server (signer)
-       References       []string `marshal:"idlist"` // cert IDs for timestamping the system
+       Issuer           string `marshal:"id"`
+       AuthorizedBy     string `marshal:"id" key:"Authorized-By"`
+       LastDebitSerial  int64  `key:"Last-Debit-Serial"`
+       LastCreditSerial int64  `key:"Last-Credit-Serial"`
+       LastCert         string `marshal:"id" key:"Last-Cert"`
+       Difference       int64
+       Draft            string   `marshal:"id"`
+       Drawer           string   `marshal:"id"`
+       DrawerCert       string   `marshal:"id" key:"Drawer-Cert"`
+       Notes            string   // TODO: server or drawer?
+       References       []string `marshal:"idlist"`
 }
 
-func DecodeClearSigned(s []byte) (c *ClearSigned, err error) {
+func ParseClearSigned(s []byte) (c *ClearSigned, err error) {
        hash, body, sig := split(s)
        if len(sig) == 0 {
-               err = fmt.Errorf("DecodeClearSigned could parse the signed document")
+               err = fmt.Errorf("ParseClearSigned could parse the signed document")
                return
        }
        c = &ClearSigned{string(hash), trimspace(dashunesc(body)), sig}
        return
 }
 
-func EncodeClearSigned(c *ClearSigned) (data []byte, err error) {
+func FormatClearSigned(c *ClearSigned) (data []byte, err error) {
        s := ClearSignedHeader
        if c.Hash != "" {
                s += "Hash: " + c.Hash + "\n"
@@ -183,7 +182,7 @@ func parse(s []byte, doctype string) (v interface{}, err error) {
 }
 
 // TODO: limit errors
-func render(v interface{}) ([]byte, error) {
+func format(v interface{}) ([]byte, error) {
        doctype := ""
        switch v.(type) {
        case *Draft:
@@ -210,8 +209,8 @@ func ParseDraft(s []byte) (draft *Draft, err error) {
        return
 }
 
-func RenderDraft(draft *Draft) ([]byte, error) {
-       return render(draft)
+func FormatDraft(draft *Draft) ([]byte, error) {
+       return format(draft)
 }
 
 func ParseCert(s []byte) (cert *Cert, err error) {
@@ -223,33 +222,32 @@ func ParseCert(s []byte) (cert *Cert, err error) {
        return
 }
 
-func RenderCert(cert *Cert) ([]byte, error) {
-       return render(cert)
-}
-
-func formatId(s string) string {
-       return fmt.Sprintf("%040X", s)
+func FormatCert(cert *Cert) ([]byte, error) {
+       return format(cert)
 }
 
 func parseId(s string) (string, error) {
-       dst := make([]byte, 20)
        if len(s) != 40 {
                return "", fmt.Errorf("parseId: expected 40 characters; got %d", len(s))
        }
+       dst := make([]byte, len(s)/2)
        _, err := hex.Decode(dst, []byte(s))
-       return string(dst), err
+       return s, err
 }
 
-func quoteValue(s string) string {
+func formatId(s string) string {
        return s
 }
 
-func unquoteValue(s string) (string, error) {
+func parseString(s string) (string, error) {
+       if len(s) > 140 {
+               return "", fmt.Errorf("parseString: 140 chars limit is exceeded")
+       }
        return s, nil
 }
 
-func formatDate(i int64) string {
-       return time.SecondsToUTC(i).Format(time.RFC3339)
+func formatString(s string) string {
+       return s
 }
 
 func parseDate(s string) (int64, error) {
@@ -260,7 +258,11 @@ func parseDate(s string) (int64, error) {
        return t.Seconds(), nil
 }
 
-func marshal(iv interface{}) []Field {
+func formatDate(i int64) string {
+       return time.SecondsToUTC(i).Format(time.RFC3339)
+}
+
+func unmarshal(fields []Field, iv interface{}) error {
        v := reflect.ValueOf(iv)
        if v.Kind() != reflect.Ptr || v.IsNil() || v.Elem().Kind() != reflect.Struct {
                panic("unmarshal: input is not a pointer to struct")
@@ -268,7 +270,9 @@ func marshal(iv interface{}) []Field {
        v = v.Elem()
        t := v.Type()
        n := v.NumField()
-       fields := []Field{}
+       if len(fields) != n {
+               return fmt.Errorf("unmarshal: %s has %d fields, got %d\n", t.Name(), n, len(fields))
+       }
        for i := 0; i < n; i++ {
                ft := t.Field(i)
                fv := v.Field(i)
@@ -277,58 +281,73 @@ func marshal(iv interface{}) []Field {
                if k == "" {
                        k = ft.Name
                }
-               val := ""
+               if fields[i].Key != k {
+                       return fmt.Errorf("unmarshal: field %d of %s (%s) is missing\n", i+1, t.Name(), k)
+               }
+               s := fields[i].Value
+               var err error
                switch fv.Kind() {
                case reflect.String:
+                       var val string
                        switch m {
                        case "id":
-                               val = formatId(fv.String())
+                               val, err = parseId(s)
                        case "":
-                               val = quoteValue(fv.String())
+                               val, err = parseString(s)
                        default:
                                panic("bad string field tag")
                        }
+                       fv.SetString(val)
                case reflect.Int, reflect.Int32, reflect.Int64:
+                       var val int64
                        switch m {
                        case "date":
-                               val = formatDate(fv.Int())
+                               val, err = parseDate(s)
                        case "":
-                               val = strconv.Itoa64(fv.Int())
+                               val, err = strconv.Atoi64(s)
                        default:
                                panic("bad int field tag")
                        }
+                       fv.SetInt(val)
                case reflect.Uint, reflect.Uint32, reflect.Uint64:
+                       var val uint64
                        switch m {
                        case "":
-                               val = strconv.Uitoa64(fv.Uint())
+                               val, err = strconv.Atoui64(s)
                        default:
                                panic("bad uint field tag")
                        }
+                       fv.SetUint(val)
                case reflect.Slice:
+                       var val []string
                        switch m {
                        case "idlist":
                                if fv.Type().Elem().Kind() != reflect.String {
                                        panic("only string slice is supported")
                                }
-                               k := fv.Len()
-                               for j := 0; j < k; j++ {
-                                       if j > 0 {
-                                               val += " "
+                               ids := strings.Split(s, " ")
+                               val = make([]string, len(ids))
+                               for j := range val {
+                                       val[j], err = parseId(ids[j])
+                                       if err != nil {
+                                               return err
                                        }
-                                       val += formatId(fv.Index(j).String())
                                }
                        default:
                                panic("bad slice field tag")
                        }
+                       fv.Set(reflect.ValueOf(val))
                default:
                        panic("bad field type")
                }
-               fields = append(fields, Field{k, val})
+               if err != nil {
+                       return err
+               }
        }
-       return fields
+       return nil
 }
 
-func unmarshal(fields []Field, iv interface{}) error {
+func marshal(iv interface{}) []Field {
        v := reflect.ValueOf(iv)
        if v.Kind() != reflect.Ptr || v.IsNil() || v.Elem().Kind() != reflect.Struct {
                panic("unmarshal: input is not a pointer to struct")
@@ -336,9 +355,7 @@ func unmarshal(fields []Field, iv interface{}) error {
        v = v.Elem()
        t := v.Type()
        n := v.NumField()
-       if len(fields) != n {
-               return fmt.Errorf("unmarshal: %s has %d fields, got %d\n", t.Name(), n, len(fields))
-       }
+       fields := []Field{}
        for i := 0; i < n; i++ {
                ft := t.Field(i)
                fv := v.Field(i)
@@ -347,70 +364,55 @@ func unmarshal(fields []Field, iv interface{}) error {
                if k == "" {
                        k = ft.Name
                }
-               if fields[i].Key != k {
-                       return fmt.Errorf("unmarshal: field %d of %s (%s) is missing\n", i, t.Name(), k)
-               }
-               s := fields[i].Value
-               var err error
+               val := ""
                switch fv.Kind() {
                case reflect.String:
-                       var val string
                        switch m {
                        case "id":
-                               val, err = parseId(s)
+                               val = formatId(fv.String())
                        case "":
-                               val, err = unquoteValue(s)
+                               val = formatString(fv.String())
                        default:
                                panic("bad string field tag")
                        }
-                       fv.SetString(val)
                case reflect.Int, reflect.Int32, reflect.Int64:
-                       var val int64
                        switch m {
                        case "date":
-                               val, err = parseDate(s)
+                               val = formatDate(fv.Int())
                        case "":
-                               val, err = strconv.Atoi64(s)
+                               val = strconv.Itoa64(fv.Int())
                        default:
                                panic("bad int field tag")
                        }
-                       fv.SetInt(val)
                case reflect.Uint, reflect.Uint32, reflect.Uint64:
-                       var val uint64
                        switch m {
                        case "":
-                               val, err = strconv.Atoui64(s)
+                               val = strconv.Uitoa64(fv.Uint())
                        default:
                                panic("bad uint field tag")
                        }
-                       fv.SetUint(val)
                case reflect.Slice:
-                       var val []string
                        switch m {
                        case "idlist":
                                if fv.Type().Elem().Kind() != reflect.String {
                                        panic("only string slice is supported")
                                }
-                               ids := strings.Split(s, " ")
-                               val = make([]string, len(ids))
-                               for j := range val {
-                                       val[j], err = parseId(ids[j])
-                                       if err != nil {
-                                               return err
+                               k := fv.Len()
+                               for j := 0; j < k; j++ {
+                                       if j > 0 {
+                                               val += " "
                                        }
+                                       val += formatId(fv.Index(j).String())
                                }
                        default:
                                panic("bad slice field tag")
                        }
-                       fv.Set(reflect.ValueOf(val))
                default:
                        panic("bad field type")
                }
-               if err != nil {
-                       return err
-               }
+               fields = append(fields, Field{k, val})
        }
-       return nil
+       return fields
 }
 
 func getLine(data []byte) (line, rest []byte) {
index 72f907b..6113fac 100644 (file)
@@ -95,7 +95,7 @@ func eqFields(f1, f2 []Field) bool {
 
 func TestClearSigned(t *testing.T) {
        for _, x := range testData {
-               c, err := DecodeClearSigned(x.D)
+               c, err := ParseClearSigned(x.D)
                if err != nil {
                        t.Errorf("decoding %#v failed: %s\n", x.D, err)
                        continue
@@ -105,7 +105,7 @@ func TestClearSigned(t *testing.T) {
                }
        }
        for _, x := range testData {
-               d, err := EncodeClearSigned(x.C)
+               d, err := FormatClearSigned(x.C)
                if err != nil {
                        t.Errorf("encoding %#v failed: %s\n", x.C, err)
                        continue
@@ -138,12 +138,13 @@ Drawer: 000000000000000000000000000000000000000A
 Beneficiary: 000000000000000000000000000000000000000B
 Amount: 1
 Denomination: half euro
-Issue-Date: 2011-11-13T12:20:35Z
-Maturity-Date: 2011-12-27T09:18:46Z
+Issuer: 000000000000000000000000000000000000000D
+Authorized-By: 000000000000000000000000000000000000000C
+Date: 2011-11-13T12:20:35Z
+Maturity-Date: 2011-11-13T12:20:35Z
+Expiry-Date: 2011-12-27T09:18:46Z
 Notes: some notes
 Nonce: 42
-Server: 000000000000000000000000000000000000000C
-Drawee: 000000000000000000000000000000000000000D
 `
 
 func TestDraft(t *testing.T) {
@@ -152,7 +153,7 @@ func TestDraft(t *testing.T) {
                t.Errorf("ParseDraft failed: %s\n", err)
                return
        }
-       s, err := RenderDraft(d)
+       s, err := FormatDraft(d)
        if err != nil {
                t.Errorf("render %v draft failed: %s\n", d, err)
        }
@@ -168,15 +169,14 @@ Serial: 13
 Date: 2011-11-01T10:29:38Z
 Balance: 23
 Denomination: half euro
-Server: 000000000000000000000000000000000000000A
 Issuer: 000000000000000000000000000000000000000B
+Authorized-By: 000000000000000000000000000000000000000A
 Last-Debit-Serial: 0
 Last-Credit-Serial: 12
 Last-Cert: 000000000000000000000000000000000000000C
 Difference: 1
 Draft: 000000000000000000000000000000000000000D
 Drawer: 000000000000000000000000000000000000000E
-Drawer-Serial: 2
 Drawer-Cert: 000000000000000000000000000000000000000F
 Notes: -
 References: 000000000000000000000000000000000000000C 000000000000000000000000000000000000000F
@@ -188,7 +188,7 @@ func TestCert(t *testing.T) {
                t.Errorf("ParseCert failed: %s\n", err)
                return
        }
-       s, err := RenderCert(c)
+       s, err := FormatCert(c)
        if err != nil {
                t.Errorf("render %v cert failed: %s\n", c, err)
        }