hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

hashing_fs.go (2723B)

    1 // Copyright 2019 The Hugo Authors. All rights reserved.
    2 //
    3 // Licensed under the Apache License, Version 2.0 (the "License");
    4 // you may not use this file except in compliance with the License.
    5 // You may obtain a copy of the License at
    6 // http://www.apache.org/licenses/LICENSE-2.0
    7 //
    8 // Unless required by applicable law or agreed to in writing, software
    9 // distributed under the License is distributed on an "AS IS" BASIS,
   10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   11 // See the License for the specific language governing permissions and
   12 // limitations under the License.
   13 
   14 package hugofs
   15 
   16 import (
   17 	"crypto/md5"
   18 	"encoding/hex"
   19 	"hash"
   20 	"os"
   21 
   22 	"github.com/spf13/afero"
   23 )
   24 
   25 var (
   26 	_ afero.Fs            = (*md5HashingFs)(nil)
   27 	_ FilesystemUnwrapper = (*md5HashingFs)(nil)
   28 )
   29 
   30 // FileHashReceiver will receive the filename an the content's MD5 sum on file close.
   31 type FileHashReceiver interface {
   32 	OnFileClose(name, md5sum string)
   33 }
   34 
   35 type md5HashingFs struct {
   36 	afero.Fs
   37 	hashReceiver FileHashReceiver
   38 }
   39 
   40 // NewHashingFs creates a new filesystem that will receive MD5 checksums of
   41 // any written file content on Close. Note that this is probably not a good
   42 // idea for "full build" situations, but when doing fast render mode, the amount
   43 // of files published is low, and it would be really nice to know exactly which
   44 // of these files where actually changed.
   45 // Note that this will only work for file operations that use the io.Writer
   46 // to write content to file, but that is fine for the "publish content" use case.
   47 func NewHashingFs(delegate afero.Fs, hashReceiver FileHashReceiver) afero.Fs {
   48 	return &md5HashingFs{Fs: delegate, hashReceiver: hashReceiver}
   49 }
   50 
   51 func (fs *md5HashingFs) UnwrapFilesystem() afero.Fs {
   52 	return fs.Fs
   53 }
   54 
   55 func (fs *md5HashingFs) Create(name string) (afero.File, error) {
   56 	f, err := fs.Fs.Create(name)
   57 	if err == nil {
   58 		f = fs.wrapFile(f)
   59 	}
   60 	return f, err
   61 }
   62 
   63 func (fs *md5HashingFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) {
   64 	f, err := fs.Fs.OpenFile(name, flag, perm)
   65 	if err == nil && isWrite(flag) {
   66 		f = fs.wrapFile(f)
   67 	}
   68 	return f, err
   69 }
   70 
   71 func (fs *md5HashingFs) wrapFile(f afero.File) afero.File {
   72 	return &hashingFile{File: f, h: md5.New(), hashReceiver: fs.hashReceiver}
   73 }
   74 
   75 func (fs *md5HashingFs) Name() string {
   76 	return "md5HashingFs"
   77 }
   78 
   79 type hashingFile struct {
   80 	hashReceiver FileHashReceiver
   81 	h            hash.Hash
   82 	afero.File
   83 }
   84 
   85 func (h *hashingFile) Write(p []byte) (n int, err error) {
   86 	n, err = h.File.Write(p)
   87 	if err != nil {
   88 		return
   89 	}
   90 	return h.h.Write(p)
   91 }
   92 
   93 func (h *hashingFile) Close() error {
   94 	sum := hex.EncodeToString(h.h.Sum(nil))
   95 	h.hashReceiver.OnFileClose(h.Name(), sum)
   96 	return h.File.Close()
   97 }