modules.go

Summary

Maintainability
A
0 mins
Test Coverage
// Copyright (c) 2020-2023 Ozan Hacıbekiroğlu.
// Use of this source code is governed by a MIT License
// that can be found in the LICENSE file.

package ugo

import (
    "errors"
)

// Importable interface represents importable module instance.
type Importable interface {
    // Import should return either an Object or module source code ([]byte).
    Import(moduleName string) (interface{}, error)
}

// ExtImporter wraps methods for a module which will be impored dynamically like
// a file.
type ExtImporter interface {
    Importable
    // Get returns Extimporter instance which will import a module.
    Get(moduleName string) ExtImporter
    // Name returns the full name of the module e.g. absoule path of a file.
    // Import names are generally relative, this overwrites module name and used
    // as unique key for compiler module cache.
    Name() string
    // Fork returns an ExtImporter instance which will be used to import the
    // modules. Fork will get the result of Name() if it is not empty, otherwise
    // module name will be same with the Get call.
    Fork(moduleName string) ExtImporter
}

// ModuleMap represents a set of named modules. Use NewModuleMap to create a
// new module map.
type ModuleMap struct {
    m  map[string]Importable
    im ExtImporter
}

// NewModuleMap creates a new module map.
func NewModuleMap() *ModuleMap {
    return &ModuleMap{m: make(map[string]Importable)}
}

// SetExtImporter sets an ExtImporter to ModuleMap, which will be used to
// import modules dynamically.
func (m *ModuleMap) SetExtImporter(im ExtImporter) *ModuleMap {
    m.im = im
    return m
}

// Fork creates a new ModuleMap instance if ModuleMap has an ExtImporter to
// make ExtImporter preseve state.
func (m *ModuleMap) Fork(moduleName string) *ModuleMap {
    if m == nil {
        return nil
    }
    if m.im != nil {
        fork := m.im.Fork(moduleName)
        return &ModuleMap{m: m.m, im: fork}
    }
    return m
}

// Add adds an importable module.
func (m *ModuleMap) Add(name string, module Importable) *ModuleMap {
    m.m[name] = module
    return m
}

// AddBuiltinModule adds a builtin module.
func (m *ModuleMap) AddBuiltinModule(
    name string,
    attrs map[string]Object,
) *ModuleMap {
    m.m[name] = &BuiltinModule{Attrs: attrs}
    return m
}

// AddSourceModule adds a source module.
func (m *ModuleMap) AddSourceModule(name string, src []byte) *ModuleMap {
    m.m[name] = &SourceModule{Src: src}
    return m
}

// Remove removes a named module.
func (m *ModuleMap) Remove(name string) {
    delete(m.m, name)
}

// Get returns an import module identified by name.
// It returns nil if the name is not found.
func (m *ModuleMap) Get(name string) Importable {
    if m == nil {
        return nil
    }

    v, ok := m.m[name]
    if ok || m.im == nil {
        return v
    }
    return m.im.Get(name)
}

// Copy creates a copy of the module map.
func (m *ModuleMap) Copy() *ModuleMap {
    c := &ModuleMap{m: make(map[string]Importable), im: m.im}

    for name, mod := range m.m {
        c.m[name] = mod
    }
    return c
}

// SourceModule is an importable module that's written in uGO.
type SourceModule struct {
    Src []byte
}

// Import returns a module source code.
func (m *SourceModule) Import(_ string) (interface{}, error) {
    return m.Src, nil
}

// BuiltinModule is an importable module that's written in Go.
type BuiltinModule struct {
    Attrs map[string]Object
}

// Import returns an immutable map for the module.
func (m *BuiltinModule) Import(moduleName string) (interface{}, error) {
    if m.Attrs == nil {
        return nil, errors.New("module attributes not set")
    }

    cp := Map(m.Attrs).Copy()
    cp.(Map)[AttrModuleName] = String(moduleName)
    return cp, nil
}