package registry import ( "bytes" "fmt" "os" "strings" "gopkg.in/yaml.v3" ) // rawFunction mirrors the YAML frontmatter of a function .md file. type rawFunction struct { Name string `yaml:"name"` Kind string `yaml:"kind"` Lang string `yaml:"lang"` Domain string `yaml:"domain"` Version string `yaml:"version"` Purity string `yaml:"purity"` Signature string `yaml:"signature"` Description string `yaml:"description"` Tags []string `yaml:"tags"` UsesFunctions []string `yaml:"uses_functions"` UsesTypes []string `yaml:"uses_types"` Returns []string `yaml:"returns"` ReturnsOptional bool `yaml:"returns_optional"` ErrorType string `yaml:"error_type"` Imports []string `yaml:"imports"` Tested bool `yaml:"tested"` Tests []string `yaml:"tests"` TestFilePath string `yaml:"test_file_path"` FilePath string `yaml:"file_path"` // Component fields Props []PropDef `yaml:"props"` Emits []string `yaml:"emits"` HasState *bool `yaml:"has_state"` Framework string `yaml:"framework"` Variant []string `yaml:"variant"` } // rawType mirrors the YAML frontmatter of a type .md file. type rawType struct { Name string `yaml:"name"` Lang string `yaml:"lang"` Domain string `yaml:"domain"` Version string `yaml:"version"` Algebraic string `yaml:"algebraic"` Definition string `yaml:"definition"` Description string `yaml:"description"` Tags []string `yaml:"tags"` UsesTypes []string `yaml:"uses_types"` FilePath string `yaml:"file_path"` } // extractFrontmatter splits a .md file into YAML frontmatter and body. func extractFrontmatter(data []byte) ([]byte, []byte, error) { content := data if !bytes.HasPrefix(content, []byte("---\n")) && !bytes.HasPrefix(content, []byte("---\r\n")) { return nil, nil, fmt.Errorf("missing opening --- in frontmatter") } // Skip opening --- rest := content[4:] idx := bytes.Index(rest, []byte("\n---")) if idx < 0 { return nil, nil, fmt.Errorf("missing closing --- in frontmatter") } fm := rest[:idx] body := rest[idx+4:] // skip \n--- return fm, body, nil } // ParseFunctionMD parses a function .md file into a Function. func ParseFunctionMD(path string) (*Function, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("reading %s: %w", path, err) } fm, body, err := extractFrontmatter(data) if err != nil { return nil, fmt.Errorf("parsing %s: %w", path, err) } var raw rawFunction if err := yaml.Unmarshal(fm, &raw); err != nil { return nil, fmt.Errorf("parsing YAML in %s: %w", path, err) } if raw.Name == "" { return nil, fmt.Errorf("%s: name is required", path) } if raw.Kind == "" { return nil, fmt.Errorf("%s: kind is required", path) } if raw.Description == "" { return nil, fmt.Errorf("%s: description is required", path) } example := extractExample(body) f := &Function{ ID: GenerateID(raw.Name, raw.Lang, raw.Domain), Name: raw.Name, Kind: Kind(raw.Kind), Lang: raw.Lang, Domain: raw.Domain, Version: raw.Version, Purity: Purity(raw.Purity), Signature: raw.Signature, Description: raw.Description, Tags: raw.Tags, UsesFunctions: raw.UsesFunctions, UsesTypes: raw.UsesTypes, Returns: raw.Returns, ReturnsOptional: raw.ReturnsOptional, ErrorType: raw.ErrorType, Imports: raw.Imports, Tested: raw.Tested, Tests: raw.Tests, TestFilePath: raw.TestFilePath, FilePath: raw.FilePath, Props: raw.Props, Emits: raw.Emits, HasState: raw.HasState, Framework: raw.Framework, Variant: raw.Variant, } if example != "" && f.Example == "" { f.Example = example } return f, nil } // ParseTypeMD parses a type .md file into a Type. func ParseTypeMD(path string) (*Type, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("reading %s: %w", path, err) } fm, _, err := extractFrontmatter(data) if err != nil { return nil, fmt.Errorf("parsing %s: %w", path, err) } var raw rawType if err := yaml.Unmarshal(fm, &raw); err != nil { return nil, fmt.Errorf("parsing YAML in %s: %w", path, err) } if raw.Name == "" { return nil, fmt.Errorf("%s: name is required", path) } if raw.Description == "" { return nil, fmt.Errorf("%s: description is required", path) } t := &Type{ ID: GenerateID(raw.Name, raw.Lang, raw.Domain), Name: raw.Name, Lang: raw.Lang, Domain: raw.Domain, Version: raw.Version, Algebraic: Algebraic(raw.Algebraic), Definition: strings.TrimSpace(raw.Definition), Description: raw.Description, Tags: raw.Tags, UsesTypes: raw.UsesTypes, FilePath: raw.FilePath, } return t, nil } // extractExample pulls the first code block after an "## Ejemplo" heading. func extractExample(body []byte) string { lines := strings.Split(string(body), "\n") inExample := false inCode := false var code []string for _, line := range lines { trimmed := strings.TrimSpace(line) if strings.HasPrefix(trimmed, "## Ejemplo") { inExample = true continue } if inExample && !inCode && strings.HasPrefix(trimmed, "```") { inCode = true continue } if inCode { if strings.HasPrefix(trimmed, "```") { return strings.Join(code, "\n") } code = append(code, line) } if inExample && !inCode && strings.HasPrefix(trimmed, "##") { break } } return "" }