mirror of
https://github.com/dolthub/dolt.git
synced 2026-02-04 10:25:17 -06:00
251 lines
6.5 KiB
Go
251 lines
6.5 KiB
Go
// Copyright 2020 Dolthub, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package doltdocs
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"path/filepath"
|
|
|
|
"github.com/dolthub/dolt/go/libraries/doltcore/dbfactory"
|
|
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
|
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
|
|
"github.com/dolthub/dolt/go/libraries/utils/filesys"
|
|
"github.com/dolthub/dolt/go/store/types"
|
|
)
|
|
|
|
var ErrDocsUpdate = errors.New("error updating local docs")
|
|
var ErrEmptyDocsTable = errors.New("error: All docs removed. Removing Docs Table")
|
|
var ErrMarshallingSchema = errors.New("error marshalling schema")
|
|
|
|
var doltDocsColumns = schema.NewColCollection(
|
|
schema.NewColumn(doltdb.DocPkColumnName, schema.DocNameTag, types.StringKind, true, schema.NotNullConstraint{}),
|
|
schema.NewColumn(doltdb.DocTextColumnName, schema.DocTextTag, types.StringKind, false),
|
|
)
|
|
var Schema = schema.MustSchemaFromCols(doltDocsColumns)
|
|
|
|
type Doc struct {
|
|
Text []byte
|
|
DocPk string
|
|
File string
|
|
}
|
|
|
|
type Docs []Doc
|
|
|
|
const (
|
|
ReadmeFile = "../README.md"
|
|
LicenseFile = "../LICENSE.md"
|
|
|
|
// LicenseDoc is the key for accessing the license within the docs table
|
|
LicenseDoc = "LICENSE.md"
|
|
// ReadmeDoc is the key for accessing the readme within the docs table
|
|
ReadmeDoc = "README.md"
|
|
)
|
|
|
|
var SupportedDocs = Docs{
|
|
{DocPk: ReadmeDoc, File: ReadmeFile},
|
|
{DocPk: LicenseDoc, File: LicenseFile},
|
|
}
|
|
|
|
// GetLocalFileText returns a byte slice representing the contents of the provided file, if it exists
|
|
func GetLocalFileText(fs filesys.Filesys, file string) ([]byte, error) {
|
|
path := ""
|
|
if DocFileExists(fs, file) {
|
|
path = GetDocFilePath(file)
|
|
}
|
|
|
|
if path != "" {
|
|
return fs.ReadFile(path)
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
// GetSupportedDocs takes in a filesystem and returns the contents of all docs on disk.
|
|
func GetSupportedDocs(fs filesys.Filesys) (docs Docs, err error) {
|
|
docs = Docs{}
|
|
for _, doc := range SupportedDocs {
|
|
newerText, err := GetLocalFileText(fs, doc.File)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
doc.Text = newerText
|
|
docs = append(docs, doc)
|
|
}
|
|
return docs, nil
|
|
}
|
|
|
|
// GetDoc takes in a filesystem and a docName and returns the doc's contents.
|
|
func GetDoc(fs filesys.Filesys, docName string) (doc Doc, err error) {
|
|
for _, doc := range SupportedDocs {
|
|
if doc.DocPk == docName {
|
|
newerText, err := GetLocalFileText(fs, doc.File)
|
|
if err != nil {
|
|
return Doc{}, err
|
|
}
|
|
doc.Text = newerText
|
|
return doc, nil
|
|
}
|
|
}
|
|
return Doc{}, err
|
|
}
|
|
|
|
func GetDocNamesFromDocs(docs Docs) []string {
|
|
if docs == nil {
|
|
return nil
|
|
}
|
|
|
|
ret := make([]string, len(docs))
|
|
|
|
for i, doc := range docs {
|
|
ret[i] = doc.DocPk
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
// GetDocsFromRoot takes in a root value and returns the docs stored in its dolt_docs table.
|
|
func GetDocsFromRoot(ctx context.Context, root *doltdb.RootValue, docNames ...string) (Docs, error) {
|
|
docTbl, docTblFound, err := root.GetTable(ctx, doltdb.DocTableName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var sch schema.Schema
|
|
if docTblFound {
|
|
docSch, err := docTbl.GetSchema(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sch = docSch
|
|
}
|
|
|
|
if docNames == nil {
|
|
docNames = GetDocNamesFromDocs(SupportedDocs)
|
|
}
|
|
|
|
docs := make(Docs, len(docNames))
|
|
for i, name := range docNames {
|
|
doc, isSupported := IsSupportedDoc(name)
|
|
if !isSupported {
|
|
return nil, fmt.Errorf("%s is not a supported doc", name)
|
|
}
|
|
|
|
docText, err := getDocTextFromTbl(ctx, docTbl, &sch, name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
doc.Text = docText
|
|
docs[i] = doc
|
|
}
|
|
|
|
return docs, nil
|
|
}
|
|
|
|
// Save takes in a fs object and saves all the docs to the filesystem, overwriting any existing files.
|
|
func (docs Docs) Save(fs filesys.ReadWriteFS) error {
|
|
for _, doc := range docs {
|
|
if _, ok := IsSupportedDoc(doc.DocPk); !ok {
|
|
continue
|
|
}
|
|
filePath := GetDocFilePath(doc.File)
|
|
if doc.Text != nil {
|
|
err := fs.WriteFile(filePath, doc.Text)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
err := DeleteDoc(fs, doc.DocPk)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetDocFilePath takes in a filename and appends it to the DoltDir filepath.
|
|
func GetDocFilePath(filename string) string {
|
|
return filepath.Join(dbfactory.DoltDir, filename)
|
|
}
|
|
|
|
// LoadDocs takes in a fs object and reads all the docs (ex. README.md) defined in SupportedDocs.
|
|
func LoadDocs(fs filesys.ReadWriteFS) (Docs, error) {
|
|
docsWithCurrentText := SupportedDocs
|
|
for i, val := range docsWithCurrentText {
|
|
path := GetDocFilePath(val.File)
|
|
exists, isDir := fs.Exists(path)
|
|
if exists && !isDir {
|
|
data, err := fs.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
val.Text = data
|
|
docsWithCurrentText[i] = val
|
|
}
|
|
}
|
|
return docsWithCurrentText, nil
|
|
}
|
|
|
|
func IsSupportedDoc(docName string) (Doc, bool) {
|
|
for _, doc := range SupportedDocs {
|
|
if doc.DocPk == docName {
|
|
return doc, true
|
|
}
|
|
}
|
|
return Doc{}, false
|
|
}
|
|
|
|
func DocFileExists(fs filesys.ReadWriteFS, file string) bool {
|
|
exists, isDir := fs.Exists(GetDocFilePath(file))
|
|
return exists && !isDir
|
|
}
|
|
|
|
// DeleteDoc takes in a filesytem object and deletes the file with docName, if it's a SupportedDoc.
|
|
func DeleteDoc(fs filesys.ReadWriteFS, docName string) error {
|
|
if doc, ok := IsSupportedDoc(docName); ok {
|
|
if doc.DocPk == docName {
|
|
path := GetDocFilePath(doc.File)
|
|
exists, isDir := fs.Exists(path)
|
|
if exists && !isDir {
|
|
return fs.DeleteFile(path)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UpdateRootWithDocs takes in a root value, and some docs and writes those docs to the dolt_docs table
|
|
// (perhaps creating it in the process). The table might not necessarily need to be created if there are no docs in the
|
|
// repo yet.
|
|
func UpdateRootWithDocs(ctx context.Context, root *doltdb.RootValue, docs Docs) (*doltdb.RootValue, error) {
|
|
docTbl, err := CreateOrUpdateDocsTable(ctx, root, docs)
|
|
|
|
if errors.Is(ErrEmptyDocsTable, err) {
|
|
root, err = root.RemoveTables(ctx, doltdb.DocTableName)
|
|
} else if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// There might not need be a need to create docs table if not docs have been created yet so check if docTbl != nil.
|
|
if docTbl != nil {
|
|
root, err = root.PutTable(ctx, doltdb.DocTableName, docTbl)
|
|
}
|
|
|
|
return root, nil
|
|
}
|