package utpl import ( "errors" "fmt" "gitea.1216.top/lxy/u-tpl/internal" ) type PlaceholderStyle = internal.PlaceholderStyle const ( QuestionMark PlaceholderStyle = internal.QuestionMark DollarNumber = internal.DollarNumber ColonNumber = internal.ColonNumber ) type IncludeResolver func(path string) (string, error) type Option func(*Engine) type Engine struct { style internal.PlaceholderStyle rawPolicy RawPolicy includeResolver IncludeResolver strict bool } func New(opts ...Option) *Engine { e := &Engine{ style: internal.QuestionMark, strict: true, } for _, opt := range opts { opt(e) } return e } func WithPlaceholderStyle(style PlaceholderStyle) Option { return func(e *Engine) { e.style = style } } func WithRawPolicy(policy RawPolicy) Option { return func(e *Engine) { e.rawPolicy = policy } } func WithIncludeResolver(resolver IncludeResolver) Option { return func(e *Engine) { e.includeResolver = resolver } } func WithStrictMode(strict bool) Option { return func(e *Engine) { e.strict = strict } } func (e *Engine) Parse(name string, source string) (*Template, error) { lexer := internal.NewLexer(source) tokens, err := lexer.Tokenize() if err != nil { return nil, wrapParseError(err, name) } var includeMgr *internal.IncludeManager if e.includeResolver != nil { includeMgr = internal.NewIncludeManager(internal.IncludeResolver(e.includeResolver)) } parser := internal.NewParser(source, tokens, includeMgr) nodes, err := parser.Parse() if err != nil { return nil, wrapParseError(err, name) } namespace := "" blocks := make(map[string][]internal.Node) var bodyNodes []internal.Node hasBlocks := false for _, n := range nodes { if ns, ok := n.(*internal.NamespaceNode); ok { namespace = ns.Name continue } if blk, ok := n.(*internal.BlockNode); ok { hasBlocks = true fullName := blk.Name if namespace != "" { fullName = namespace + "." + blk.Name } blocks[fullName] = blk.Body continue } bodyNodes = append(bodyNodes, n) } return &Template{ name: name, engine: e, nodes: bodyNodes, blocks: blocks, hasBlocks: hasBlocks, namespace: namespace, }, nil } func (e *Engine) MustParse(name string, source string) *Template { tpl, err := e.Parse(name, source) if err != nil { panic(err) } return tpl } func wrapParseError(err error, name string) error { if _, ok := err.(*ParseError); ok { return err } msg := fmt.Sprintf("template %q: %s", name, err.Error()) var posErr *internal.PosError if errors.As(err, &posErr) { return &ParseError{ Pos: Position{Line: posErr.Line, Column: posErr.Col}, Message: msg, } } return &ParseError{ Pos: Position{}, Message: msg, } }