github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/source/fileInfo.go (about) 1 // Copyright 2021 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 source 15 16 import ( 17 "fmt" 18 "path/filepath" 19 "strings" 20 "sync" 21 "time" 22 23 "github.com/bep/gitmap" 24 "github.com/gohugoio/hugo/common/paths" 25 26 "github.com/gohugoio/hugo/hugofs/files" 27 28 "github.com/gohugoio/hugo/common/hugio" 29 30 "github.com/gohugoio/hugo/hugofs" 31 32 "github.com/gohugoio/hugo/helpers" 33 ) 34 35 // fileInfo implements the File interface. 36 var ( 37 _ File = (*FileInfo)(nil) 38 ) 39 40 // File represents a source file. 41 // This is a temporary construct until we resolve page.Page conflicts. 42 // TODO(bep) remove this construct once we have resolved page deprecations 43 type File interface { 44 fileOverlap 45 FileWithoutOverlap 46 } 47 48 // Temporary to solve duplicate/deprecated names in page.Page 49 type fileOverlap interface { 50 // Path gets the relative path including file name and extension. 51 // The directory is relative to the content root. 52 Path() string 53 54 // Section is first directory below the content root. 55 // For page bundles in root, the Section will be empty. 56 Section() string 57 58 // Lang is the language code for this page. It will be the 59 // same as the site's language code. 60 Lang() string 61 62 IsZero() bool 63 } 64 65 type FileWithoutOverlap interface { 66 67 // Filename gets the full path and filename to the file. 68 Filename() string 69 70 // Dir gets the name of the directory that contains this file. 71 // The directory is relative to the content root. 72 Dir() string 73 74 // Extension is an alias to Ext(). 75 // Deprecated: Use Ext instead. 76 Extension() string 77 78 // Ext gets the file extension, i.e "myblogpost.md" will return "md". 79 Ext() string 80 81 // LogicalName is filename and extension of the file. 82 LogicalName() string 83 84 // BaseFileName is a filename without extension. 85 BaseFileName() string 86 87 // TranslationBaseName is a filename with no extension, 88 // not even the optional language extension part. 89 TranslationBaseName() string 90 91 // ContentBaseName is a either TranslationBaseName or name of containing folder 92 // if file is a leaf bundle. 93 ContentBaseName() string 94 95 // UniqueID is the MD5 hash of the file's path and is for most practical applications, 96 // Hugo content files being one of them, considered to be unique. 97 UniqueID() string 98 99 // For internal use only. 100 FileInfo() hugofs.FileMetaInfo 101 } 102 103 // FileInfo describes a source file. 104 type FileInfo struct { 105 106 // Absolute filename to the file on disk. 107 filename string 108 109 sp *SourceSpec 110 111 fi hugofs.FileMetaInfo 112 113 // Derived from filename 114 ext string // Extension without any "." 115 lang string 116 117 name string 118 119 dir string 120 relDir string 121 relPath string 122 baseName string 123 translationBaseName string 124 contentBaseName string 125 section string 126 classifier files.ContentClass 127 128 uniqueID string 129 130 lazyInit sync.Once 131 } 132 133 // Filename returns a file's absolute path and filename on disk. 134 func (fi *FileInfo) Filename() string { return fi.filename } 135 136 // Path gets the relative path including file name and extension. The directory 137 // is relative to the content root. 138 func (fi *FileInfo) Path() string { return fi.relPath } 139 140 // Dir gets the name of the directory that contains this file. The directory is 141 // relative to the content root. 142 func (fi *FileInfo) Dir() string { return fi.relDir } 143 144 // Extension is an alias to Ext(). 145 func (fi *FileInfo) Extension() string { 146 helpers.Deprecated(".File.Extension", "Use .File.Ext instead. ", false) 147 return fi.Ext() 148 } 149 150 // Ext returns a file's extension without the leading period (ie. "md"). 151 func (fi *FileInfo) Ext() string { return fi.ext } 152 153 // Lang returns a file's language (ie. "sv"). 154 func (fi *FileInfo) Lang() string { return fi.lang } 155 156 // LogicalName returns a file's name and extension (ie. "page.sv.md"). 157 func (fi *FileInfo) LogicalName() string { return fi.name } 158 159 // BaseFileName returns a file's name without extension (ie. "page.sv"). 160 func (fi *FileInfo) BaseFileName() string { return fi.baseName } 161 162 // TranslationBaseName returns a file's translation base name without the 163 // language segment (ie. "page"). 164 func (fi *FileInfo) TranslationBaseName() string { return fi.translationBaseName } 165 166 // ContentBaseName is a either TranslationBaseName or name of containing folder 167 // if file is a leaf bundle. 168 func (fi *FileInfo) ContentBaseName() string { 169 fi.init() 170 return fi.contentBaseName 171 } 172 173 // Section returns a file's section. 174 func (fi *FileInfo) Section() string { 175 fi.init() 176 return fi.section 177 } 178 179 // UniqueID returns a file's unique, MD5 hash identifier. 180 func (fi *FileInfo) UniqueID() string { 181 fi.init() 182 return fi.uniqueID 183 } 184 185 // FileInfo returns a file's underlying os.FileInfo. 186 // For internal use only. 187 func (fi *FileInfo) FileInfo() hugofs.FileMetaInfo { return fi.fi } 188 189 func (fi *FileInfo) String() string { return fi.BaseFileName() } 190 191 // Open implements ReadableFile. 192 func (fi *FileInfo) Open() (hugio.ReadSeekCloser, error) { 193 f, err := fi.fi.Meta().Open() 194 195 return f, err 196 } 197 198 func (fi *FileInfo) IsZero() bool { 199 return fi == nil 200 } 201 202 // We create a lot of these FileInfo objects, but there are parts of it used only 203 // in some cases that is slightly expensive to construct. 204 func (fi *FileInfo) init() { 205 fi.lazyInit.Do(func() { 206 relDir := strings.Trim(fi.relDir, helpers.FilePathSeparator) 207 parts := strings.Split(relDir, helpers.FilePathSeparator) 208 var section string 209 if (fi.classifier != files.ContentClassLeaf && len(parts) == 1) || len(parts) > 1 { 210 section = parts[0] 211 } 212 fi.section = section 213 214 if fi.classifier.IsBundle() && len(parts) > 0 { 215 fi.contentBaseName = parts[len(parts)-1] 216 } else { 217 fi.contentBaseName = fi.translationBaseName 218 } 219 220 fi.uniqueID = helpers.MD5String(filepath.ToSlash(fi.relPath)) 221 }) 222 } 223 224 // NewTestFile creates a partially filled File used in unit tests. 225 // TODO(bep) improve this package 226 func NewTestFile(filename string) *FileInfo { 227 base := filepath.Base(filepath.Dir(filename)) 228 return &FileInfo{ 229 filename: filename, 230 translationBaseName: base, 231 } 232 } 233 234 func (sp *SourceSpec) NewFileInfoFrom(path, filename string) (*FileInfo, error) { 235 meta := &hugofs.FileMeta{ 236 Filename: filename, 237 Path: path, 238 } 239 240 return sp.NewFileInfo(hugofs.NewFileMetaInfo(nil, meta)) 241 } 242 243 func (sp *SourceSpec) NewFileInfo(fi hugofs.FileMetaInfo) (*FileInfo, error) { 244 m := fi.Meta() 245 246 filename := m.Filename 247 relPath := m.Path 248 249 if relPath == "" { 250 return nil, fmt.Errorf("no Path provided by %v (%T)", m, m.Fs) 251 } 252 253 if filename == "" { 254 return nil, fmt.Errorf("no Filename provided by %v (%T)", m, m.Fs) 255 } 256 257 relDir := filepath.Dir(relPath) 258 if relDir == "." { 259 relDir = "" 260 } 261 if !strings.HasSuffix(relDir, helpers.FilePathSeparator) { 262 relDir = relDir + helpers.FilePathSeparator 263 } 264 265 lang := m.Lang 266 translationBaseName := m.TranslationBaseName 267 268 dir, name := filepath.Split(relPath) 269 if !strings.HasSuffix(dir, helpers.FilePathSeparator) { 270 dir = dir + helpers.FilePathSeparator 271 } 272 273 ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(name), ".")) 274 baseName := paths.Filename(name) 275 276 if translationBaseName == "" { 277 // This is usually provided by the filesystem. But this FileInfo is also 278 // created in a standalone context when doing "hugo new". This is 279 // an approximate implementation, which is "good enough" in that case. 280 fileLangExt := filepath.Ext(baseName) 281 translationBaseName = strings.TrimSuffix(baseName, fileLangExt) 282 } 283 284 f := &FileInfo{ 285 sp: sp, 286 filename: filename, 287 fi: fi, 288 lang: lang, 289 ext: ext, 290 dir: dir, 291 relDir: relDir, // Dir() 292 relPath: relPath, // Path() 293 name: name, 294 baseName: baseName, // BaseFileName() 295 translationBaseName: translationBaseName, 296 classifier: m.Classifier, 297 } 298 299 return f, nil 300 } 301 302 func NewGitInfo(info gitmap.GitInfo) GitInfo { 303 return GitInfo(info) 304 } 305 306 // GitInfo provides information about a version controlled source file. 307 type GitInfo struct { 308 // Commit hash. 309 Hash string `json:"hash"` 310 // Abbreviated commit hash. 311 AbbreviatedHash string `json:"abbreviatedHash"` 312 // The commit message's subject/title line. 313 Subject string `json:"subject"` 314 // The author name, respecting .mailmap. 315 AuthorName string `json:"authorName"` 316 // The author email address, respecting .mailmap. 317 AuthorEmail string `json:"authorEmail"` 318 // The author date. 319 AuthorDate time.Time `json:"authorDate"` 320 // The commit date. 321 CommitDate time.Time `json:"commitDate"` 322 } 323 324 // IsZero returns true if the GitInfo is empty, 325 // meaning it will also be falsy in the Go templates. 326 func (g GitInfo) IsZero() bool { 327 return g.Hash == "" 328 }