1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
import dataclasses
from enum import Enum
from textwrap import dedent
from typing import ClassVar, NamedTuple

from common import cxx_literal, generate_file, load_data, get_argument_parser


class FeatureTypeNames(NamedTuple):
    code_tag: str
    doc_tag: str


class TimelineEvent(NamedTuple):
    date: str
    release: str
    message: str
    cls: list[int]


class FeatureType(FeatureTypeNames, Enum):
    experimental = FeatureTypeNames("Xp", "xp")
    deprecated = FeatureTypeNames("Dep", "dp")


@dataclasses.dataclass
class ExtraFeature:
    name: str
    internal_name: str
    documentation: str
    timeline: list[TimelineEvent] = dataclasses.field(default_factory=list)

    type: ClassVar[FeatureType]

    @property
    def code(self) -> str:
        return dedent(f"""
            {{
                .tag = {ExtraFeature.type.code_tag}::{self.internal_name},
                .name = {cxx_literal(self.name)},
                .description = {cxx_literal(self.documentation)},
            }},
        """)

    @property
    def docs(self) -> str:
        timeline = (
            f"""

            ### Timeline

            {
                "\n            ".join(
                    [
                        f"- {event.date}, {event.release}: {event.message} [{", ".join([f'[CL {cl}](https://gerrit.lix.systems/c/lix/+/{cl})' for cl in event.cls])}]"
                        for event in self.timeline
                    ]
                )
            }
        """
            if self.timeline
            else ""
        )
        return dedent(f"""
            ## [`{self.name}`]{{#{ExtraFeature.type.doc_tag}-feature-{self.name}}}

            {self.documentation.replace("\n", f"\n{'    ' * 3}")}

            {timeline}

        """)

    @property
    def short_docs(self) -> str:
        return f"  - [`{self.name}`](@docroot@/contributing/{ExtraFeature.type.name}-features.md#{ExtraFeature.type.doc_tag}-feature-{self.name})\n"


def main():
    ap = get_argument_parser()
    ap.add_argument("--deprecated", action="store_true", help="Generate deprecated features")
    ap.add_argument("--impl-header", help="Path of the implementation header to generate")
    ap.add_argument("--shortlist", help="Path of the shortlist file to generate")
    args = ap.parse_args()

    ExtraFeature.type = FeatureType.deprecated if args.deprecated else FeatureType.experimental

    def load(**kwargs) -> ExtraFeature:
        kwargs["timeline"] = [TimelineEvent(**args) for args in kwargs.get("timeline", [])]
        return ExtraFeature(**kwargs)

    features = load_data(args.defs, load)

    generate_file(
        args.header,
        features,
        lambda feature: feature.name,
        lambda feature: f"    {feature.internal_name},\n",
    )
    generate_file(args.impl_header, features, lambda feature: feature.name, lambda f: f.code)
    generate_file(args.docs, features, lambda feature: feature.name, lambda f: f.docs)
    generate_file(args.shortlist, features, lambda feature: feature.name, lambda f: f.short_docs)


if __name__ == "__main__":
    main()