Skip to content

Commit

Permalink
feat: add dynamic directive resolvers
Browse files Browse the repository at this point in the history
  • Loading branch information
vberlier committed Nov 5, 2021
1 parent 2ee4299 commit b2b6d21
Show file tree
Hide file tree
Showing 12 changed files with 176 additions and 186 deletions.
14 changes: 14 additions & 0 deletions examples/with_beet/beet.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@
"load": ["isolated/plugin_test.md"]
}
}
},
{
"require": ["beet.contrib.messages"],
"pipeline": ["lectern", "beet.contrib.render"],
"meta": {
"lectern": {
"load": ["isolated/message_test.md"]
},
"render": {
"data_pack": {
"functions": ["*"]
}
}
}
}
],
"meta": {
Expand Down
13 changes: 13 additions & 0 deletions examples/with_beet/isolated/message_test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Example with data pack messages

`@message isolated:my_greeting`

```json
["", { "text": "hello", "color": "red" }]
```

`@function isolated:message_demo`

```mcfunction
tellraw @a {{ "isolated:my_greeting" | msg }}
```
4 changes: 2 additions & 2 deletions lectern/contrib/relative_location.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from beet import Context

from lectern import AnyDirective, Document, Fragment, NamespacedResourceDirective
from lectern import Directive, Document, Fragment, NamespacedResourceDirective


def beet_default(ctx: Context):
Expand All @@ -28,7 +28,7 @@ class RelativeNamespacedResourceLoader:
def __call__(
self,
fragment: Fragment,
directives: Mapping[str, AnyDirective],
directives: Mapping[str, Directive],
) -> Fragment:
if isinstance(directives[fragment.directive], NamespacedResourceDirective):
name = fragment.expect("name")
Expand Down
4 changes: 2 additions & 2 deletions lectern/contrib/yaml_to_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
from beet import Context, JsonFile

from lectern import (
AnyDirective,
DataPackDirective,
Directive,
Document,
Fragment,
NamespacedResourceDirective,
Expand All @@ -27,7 +27,7 @@ def beet_default(ctx: Context):
document.loaders.append(handle_yaml)


def handle_yaml(fragment: Fragment, directives: Mapping[str, AnyDirective]) -> Fragment:
def handle_yaml(fragment: Fragment, directives: Mapping[str, Directive]) -> Fragment:
"""Loader that converts yaml to json."""
directive = directives[fragment.directive]
is_yaml = False
Expand Down
158 changes: 51 additions & 107 deletions lectern/directive.py
Original file line number Diff line number Diff line change
@@ -1,75 +1,24 @@
__all__ = [
"Directive",
"AnyDirective",
"DirectiveRegistry",
"NamespacedResourceDirective",
"DataPackDirective",
"ResourcePackDirective",
"BundleFragmentMixin",
"SkipDirective",
"get_builtin_directives",
]


import io
from dataclasses import dataclass
from typing import Any, Dict, Protocol, Type, Union
from typing import Any, Callable, Dict, List, Optional, Protocol, Type
from zipfile import ZipFile

from beet import DataPack, ResourcePack
from beet.library.base import NamespaceFile
from beet.library.data_pack import (
Advancement,
Biome,
BlockTag,
ConfiguredCarver,
ConfiguredFeature,
ConfiguredStructureFeature,
ConfiguredSurfaceBuilder,
Dimension,
DimensionType,
EntityTypeTag,
FluidTag,
Function,
FunctionTag,
ItemModifier,
ItemTag,
LootTable,
NoiseSettings,
Predicate,
ProcessorList,
Recipe,
Structure,
TemplatePool,
)
from beet.library.resource_pack import (
Blockstate,
Font,
FragmentShader,
GlslShader,
GlyphSizeFile,
Language,
Model,
Particle,
Shader,
ShaderPost,
Sound,
Text,
Texture,
TextureMcmeta,
TrueTypeFont,
VertexShader,
)
from beet import Container, DataPack, NamespaceFile, ResourcePack
from beet.core.utils import snake_case

from .fragment import Fragment

AnyDirective = Union[
"Directive",
"NamespacedResourceDirective",
"DataPackDirective",
"ResourcePackDirective",
"SkipDirective",
]


class Directive(Protocol):
"""Protocol for detecting directives."""
Expand All @@ -78,6 +27,53 @@ def __call__(self, fragment: Fragment, assets: ResourcePack, data: DataPack):
...


class DirectiveRegistry(Container[str, Directive]):
"""Registry for directives."""

resolvers: List[Callable[["DirectiveRegistry"], Any]]
assets: ResourcePack
data: DataPack

def __init__(
self,
assets: Optional[ResourcePack] = None,
data: Optional[DataPack] = None,
):
super().__init__()
self.resolvers = []
self.assets = assets or ResourcePack()
self.data = data or DataPack()

self["data_pack"] = DataPackDirective()
self["resource_pack"] = ResourcePackDirective()
self["skip"] = SkipDirective()

@self.add_resolver
def _(self: DirectiveRegistry):
for pack in [self.assets, self.data]:
for file_type in pack.resolve_scope_map().values():
name = snake_case(file_type.__name__)
self[name] = NamespacedResourceDirective(file_type)

def resolve(self) -> "DirectiveRegistry":
"""Resolve all directives in the registry."""
for callback in self.resolvers:
callback(self)
return self

def add_resolver(self, resolver: Callable[["DirectiveRegistry"], Any]):
"""Add directive resolver."""
self.resolvers.append(resolver)

def get_serialization_mapping(self) -> Dict[Type[NamespaceFile], str]:
"""Return the serialization mapping."""
return {
directive.file_type: directive_name
for directive_name, directive in self.items()
if isinstance(directive, NamespacedResourceDirective)
}


@dataclass
class NamespacedResourceDirective:
"""Directive for including namespaced resources."""
Expand Down Expand Up @@ -141,55 +137,3 @@ class SkipDirective:

def __call__(self, fragment: Fragment, assets: ResourcePack, data: DataPack):
fragment.expect()


def get_builtin_directives() -> Dict[str, AnyDirective]:
"""Return the built-in directives."""
return {
# fmt: off
"advancement": NamespacedResourceDirective(Advancement),
"function": NamespacedResourceDirective(Function),
"loot_table": NamespacedResourceDirective(LootTable),
"predicate": NamespacedResourceDirective(Predicate),
"recipe": NamespacedResourceDirective(Recipe),
"structure": NamespacedResourceDirective(Structure),
"block_tag": NamespacedResourceDirective(BlockTag),
"entity_type_tag": NamespacedResourceDirective(EntityTypeTag),
"fluid_tag": NamespacedResourceDirective(FluidTag),
"function_tag": NamespacedResourceDirective(FunctionTag),
"item_tag": NamespacedResourceDirective(ItemTag),
"dimension_type": NamespacedResourceDirective(DimensionType),
"dimension": NamespacedResourceDirective(Dimension),
"biome": NamespacedResourceDirective(Biome),
"configured_carver": NamespacedResourceDirective(ConfiguredCarver),
"configured_feature": NamespacedResourceDirective(ConfiguredFeature),
"configured_structure_feature": NamespacedResourceDirective(ConfiguredStructureFeature),
"configured_surface_builder": NamespacedResourceDirective(ConfiguredSurfaceBuilder),
"noise_settings": NamespacedResourceDirective(NoiseSettings),
"processor_list": NamespacedResourceDirective(ProcessorList),
"template_pool": NamespacedResourceDirective(TemplatePool),
"item_modifier": NamespacedResourceDirective(ItemModifier),

"blockstate": NamespacedResourceDirective(Blockstate),
"model": NamespacedResourceDirective(Model),
"language": NamespacedResourceDirective(Language),
"font": NamespacedResourceDirective(Font),
"glyph_sizes": NamespacedResourceDirective(GlyphSizeFile),
"truetype_font": NamespacedResourceDirective(TrueTypeFont),
"shader_post": NamespacedResourceDirective(ShaderPost),
"shader": NamespacedResourceDirective(Shader),
"fragment_shader": NamespacedResourceDirective(FragmentShader),
"vertex_shader": NamespacedResourceDirective(VertexShader),
"glsl_shader": NamespacedResourceDirective(GlslShader),
"text": NamespacedResourceDirective(Text),
"texture_mcmeta": NamespacedResourceDirective(TextureMcmeta),
"texture": NamespacedResourceDirective(Texture),
"sound": NamespacedResourceDirective(Sound),
"particle": NamespacedResourceDirective(Particle),

"data_pack": DataPackDirective(),
"resource_pack": ResourcePackDirective(),

"skip": SkipDirective(),
# fmt: on
}
34 changes: 16 additions & 18 deletions lectern/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,12 @@

from dataclasses import InitVar, dataclass, field
from pathlib import Path
from typing import (
Any,
Dict,
List,
Literal,
MutableMapping,
Optional,
Tuple,
Union,
overload,
)
from typing import Any, Dict, List, Literal, Optional, Tuple, Union, overload

from beet import Cache, Context, DataPack, File, ResourcePack
from beet.core.utils import FileSystemPath, extra_field

from .directive import AnyDirective, get_builtin_directives
from .directive import DirectiveRegistry
from .extract import FragmentLoader, MarkdownExtractor, TextExtractor
from .serialize import ExternalFilesManager, MarkdownSerializer, TextSerializer

Expand All @@ -40,9 +30,7 @@ class Document:
data: DataPack = field(default_factory=DataPack)

loaders: List[FragmentLoader] = extra_field(default_factory=list)
directives: MutableMapping[str, AnyDirective] = extra_field(
default_factory=get_builtin_directives
)
directives: DirectiveRegistry = extra_field(default_factory=DirectiveRegistry)

text_extractor: TextExtractor = extra_field(default_factory=TextExtractor)
markdown_extractor: MarkdownExtractor = extra_field(
Expand All @@ -68,9 +56,14 @@ def __post_init__(
self.data = ctx.data
if cache is None:
cache = ctx.cache["lectern"]

if cache:
self.text_extractor.cache = cache
self.markdown_extractor.cache = cache

self.directives.assets = self.assets
self.directives.data = self.data

if path:
self.load(path)
if text:
Expand All @@ -90,7 +83,7 @@ def add_text(self, source: str):
"""Extract pack fragments from plain text."""
assets, data = self.text_extractor.extract(
source=source,
directives=self.directives,
directives=self.directives.resolve(),
loaders=self.loaders,
)
self.assets.merge(assets)
Expand All @@ -104,7 +97,7 @@ def add_markdown(
"""Extract pack fragments from markdown."""
assets, data = self.markdown_extractor.extract(
source=source,
directives=self.directives,
directives=self.directives.resolve(),
loaders=self.loaders,
external_files=external_files,
)
Expand All @@ -113,7 +106,11 @@ def add_markdown(

def get_text(self) -> str:
"""Turn the data pack and the resource pack into text."""
return self.text_serializer.serialize(self.assets, self.data)
return self.text_serializer.serialize(
assets=self.assets,
data=self.data,
mapping=self.directives.resolve().get_serialization_mapping(),
)

@overload
def get_markdown(
Expand All @@ -140,6 +137,7 @@ def get_markdown(
content = self.markdown_serializer.serialize(
assets=self.assets,
data=self.data,
mapping=self.directives.resolve().get_serialization_mapping(),
external_files=external_files,
external_prefix=prefix,
)
Expand Down
4 changes: 2 additions & 2 deletions lectern/prefetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from beet import BinaryFile, Cache, File
from beet.core.utils import FileSystemPath

from .directive import Directive, get_builtin_directives
from .directive import Directive, DirectiveRegistry
from .extract import MarkdownExtractor
from .fragment import Fragment
from .serialize import ExternalFilesManager, SerializedFile
Expand All @@ -37,7 +37,7 @@ def process_file(
):
"""Prefetch urls in the specified document."""
if directives is None:
directives = get_builtin_directives()
directives = DirectiveRegistry().resolve()

path = Path(path).resolve()
output = Path(output).resolve()
Expand Down
Loading

0 comments on commit b2b6d21

Please sign in to comment.