package utils import ( "bufio" "bytes" "fmt" "io/ioutil" "os" "path/filepath" "regexp" "strings" "text/template" "gopkg.in/yaml.v2" ) type FrontMatter struct { Title string `yaml:"title"` Rate float64 `yaml:"rate"` Progress string `yaml:"progress"` Episode string `yaml:"episode"` Date string `yaml:"date"` } func GenerateMarkdown(templatePath string, outputPath string, data map[string]interface{}) error { // Define a template funcition that generates image versions funcMap := template.FuncMap{ "imageSize": func(imagePath string, size string) string { base := strings.TrimSuffix(imagePath, filepath.Ext(imagePath)) return fmt.Sprintf("%s-%s.jpg", base, size) }, } tmpl, err := template.New(filepath.Base(templatePath)).Funcs(funcMap).ParseFiles(templatePath) if err != nil { return err } var output bytes.Buffer err = tmpl.Execute(&output, data) if err != nil { return err } return os.WriteFile(outputPath, output.Bytes(), 0644) } // LoadMarkdown loads the frontmatter and content from an existing markdown file func LoadMarkdown(filepath string) (FrontMatter, string, error) { var frontmatter FrontMatter var contentBuilder strings.Builder inFrontmatter := false frontmatterComplete := false file, err := os.Open(filepath) if err != nil { return frontmatter, "", err } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() // Detect the start and end of the frontmatter block if line == "---" && !inFrontmatter { inFrontmatter = true continue } else if line == "---" && inFrontmatter { frontmatterComplete = true continue } if inFrontmatter && !frontmatterComplete { // Continue reading the frontmatter contentBuilder.WriteString(line + "\n") } else if frontmatterComplete { // Read the rest of the markdown content contentBuilder.WriteString(line + "\n") } } // Unmarshal frontmatter into the struct if err := yaml.Unmarshal([]byte(contentBuilder.String()), &frontmatter); err != nil { return frontmatter, "", fmt.Errorf("error unmarshalling frontmatter: %v", err) } // Read the markdown content contentBytes, err := ioutil.ReadAll(file) if err != nil { return frontmatter, "", err } return frontmatter, string(contentBytes), nil } // SaveUpdatedMarkdown updates only the specific fields in the frontmatter without altering the content func SaveUpdatedMarkdown(filepath string, frontmatter FrontMatter, content string) error { // Read the entire file as a string fileBytes, err := ioutil.ReadFile(filepath) if err != nil { return err } // Convert file content to string fileContent := string(fileBytes) fieldPatterns := map[string]string{ "progress": `(?m)^progress: "(.*)%"$`, "episode": `(?m)^episode: "(.*)"$`, "date": `(?m)^date: (.*)$`, "rate": `(?m)^rate: (\d+\.?\d*)$`, } // Fields replacement fileContent = updateFieldInFrontmatter(fileContent, "progress", fmt.Sprintf("\"%s\"", frontmatter.Progress), fieldPatterns["progress"]) fileContent = updateFieldInFrontmatter(fileContent, "episode", fmt.Sprintf("\"%s\"", frontmatter.Episode), fieldPatterns["episode"]) fileContent = updateFieldInFrontmatter(fileContent, "date", fmt.Sprintf("\"%s\"", frontmatter.Date), fieldPatterns["date"]) fileContent = updateFieldInFrontmatterFloat(fileContent, "rate", frontmatter.Rate, fieldPatterns["rate"]) // Write the updated content back to the file err = ioutil.WriteFile(filepath, []byte(fileContent), 0644) if err != nil { return err } return nil } func updateFieldInFrontmatter(content, field, newValue, pattern string) string { re := regexp.MustCompile(pattern) return re.ReplaceAllString(content, fmt.Sprintf("%s: %s", field, newValue)) } // Helper function to update a float64 field in the frontmatter (for numerical values like rate) func updateFieldInFrontmatterFloat(fileContent string, field string, newValue float64, pattern string) string { re := regexp.MustCompile(pattern) updatedContent := re.ReplaceAllString(fileContent, fmt.Sprintf("%s: %.1f", field, newValue)) return updatedContent }