package mfer import ( "bytes" "compress/gzip" "crypto/sha256" "errors" "time" "google.golang.org/protobuf/proto" ) //go:generate protoc --go_out=. --go_opt=paths=source_relative mf.proto // rot13("MANIFEST") const MAGIC string = "ZNAVSRFG" func newTimestampFromTime(t time.Time) *Timestamp { out := &Timestamp{ Seconds: t.Unix(), Nanos: int32(t.UnixNano() - (t.Unix() * 1000000000)), } return out } func (m *manifest) generate() error { if m.pbInner == nil { e := m.generateInner() if e != nil { return e } } if m.pbOuter == nil { e := m.generateOuter() if e != nil { return e } } dat, err := proto.MarshalOptions{Deterministic: true}.Marshal(m.pbOuter) if err != nil { return err } m.output = bytes.NewBuffer([]byte(MAGIC)) _, err = m.output.Write(dat) if err != nil { return err } return nil } func (m *manifest) generateOuter() error { if m.pbInner == nil { return errors.New("internal error") } innerData, err := proto.MarshalOptions{Deterministic: true}.Marshal(m.pbInner) if err != nil { return err } h := sha256.New() h.Write(innerData) idc := new(bytes.Buffer) gzw, err := gzip.NewWriterLevel(idc, gzip.BestCompression) if err != nil { return err } _, err = gzw.Write(innerData) if err != nil { return err } gzw.Close() o := &MFFileOuter{ InnerMessage: idc.Bytes(), Size: int64(len(innerData)), Sha256: h.Sum(nil), Version: MFFileOuter_VERSION_ONE, CompressionType: MFFileOuter_COMPRESSION_GZIP, } m.pbOuter = o return nil } func (m *manifest) generateInner() error { m.pbInner = &MFFile{ Version: MFFile_VERSION_ONE, CreatedAt: newTimestampFromTime(time.Now()), Files: []*MFFilePath{}, } for _, f := range m.files { nf := &MFFilePath{ Path: f.path, // FIXME add more stuff } m.pbInner.Files = append(m.pbInner.Files, nf) } return nil }