001/* 002 * SPDX-License-Identifier: Apache-2.0 003 * 004 * Copyright 2023-2026 The Enola <https://enola.dev> Authors 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package dev.enola.cli; 019 020import static dev.enola.core.thing.ListThingService.ENOLA_ROOT_LIST_THINGS; 021 022import com.google.protobuf.InvalidProtocolBufferException; 023 024import dev.enola.common.function.CheckedPredicate; 025import dev.enola.core.meta.docgen.Options; 026import dev.enola.core.proto.EnolaServiceGrpc.EnolaServiceBlockingStub; 027import dev.enola.core.proto.GetThingRequest; 028import dev.enola.model.Datatypes; 029import dev.enola.thing.gen.markdown.MarkdownSiteGenerator; 030import dev.enola.thing.message.MoreThings; 031import dev.enola.thing.message.ProtoThingMetadataProvider; 032import dev.enola.thing.proto.Thing; 033import dev.enola.thing.template.Templates; 034import dev.enola.web.EnolaThingProvider; 035 036import picocli.CommandLine; 037import picocli.CommandLine.Command; 038import picocli.CommandLine.Option; 039 040import java.io.IOException; 041import java.net.URI; 042import java.util.Collection; 043import java.util.function.Function; 044import java.util.stream.Collectors; 045 046@Command(name = "docgen", description = "Generate Markdown Documentation") 047public class DocGenCommand extends CommandWithModelAndOutput { 048 049 // TODO Replace "docgen" completely with separate class GenCommand subcommands 050 051 @Option( 052 names = {"--diagram", "-d"}, 053 required = true, 054 defaultValue = "Mermaid", 055 description = 056 "Type of diagrams to generate (${COMPLETION-CANDIDATES}; default:" 057 + " ${DEFAULT-VALUE})") 058 Options.DiagramType diagram; 059 060 @Option( 061 names = {"--variables", "-var"}, 062 required = true, 063 defaultValue = "Mustache", 064 description = 065 "Type of variables to generate for IRI Templates (${COMPLETION-CANDIDATES};" 066 + " default: ${DEFAULT-VALUE})") 067 Templates.Format variables; 068 069 @Option( 070 names = {"--header", "-h"}, 071 required = true, 072 defaultValue = "string:%23%20Models%0A", 073 showDefaultValue = CommandLine.Help.Visibility.ALWAYS, 074 description = "URI of Markdown header (e.g. file: or string:<URL-encoded> etc.)") 075 URI headerURI; 076 077 @Option( 078 names = {"--index", "-i"}, 079 negatable = true, 080 required = true, 081 defaultValue = "true", 082 fallbackValue = "true", 083 description = "Whether index.md should be generated") 084 boolean generateIndexFile; 085 086 @Override 087 public void run(EnolaServiceBlockingStub service) throws Exception { 088 multipleMDDocsForThings(service, generateIndexFile); 089 } 090 091 private void multipleMDDocsForThings( 092 EnolaServiceBlockingStub service, boolean generateIndexFile) throws Exception { 093 var mdp = getMetadataProvider(new EnolaThingProvider(service)); 094 var pmdp = new ProtoThingMetadataProvider(mdp); 095 var mdsg = 096 new MarkdownSiteGenerator( 097 Output.get(output), rp, mdp, pmdp, Datatypes.DTR, variables); 098 099 var things = getThings(service, ENOLA_ROOT_LIST_THINGS); 100 101 // TODO This works, but is not efficient if there were a HUGE amount of Things and MDs... 102 var map = things.stream().collect(Collectors.toMap(Thing::getIri, Function.identity())); 103 CheckedPredicate<String, IOException> isDocumentedIRI = 104 iri -> templateService.get(iri) != null; 105 mdsg.generate(things, map::get, isDocumentedIRI, templateService, generateIndexFile, true); 106 } 107 108 private Collection<Thing> getThings(EnolaServiceBlockingStub service, String iri) 109 throws InvalidProtocolBufferException { 110 var request = GetThingRequest.newBuilder().setIri(iri).build(); 111 var response = service.getThing(request); 112 return MoreThings.fromAny(response.getThing()); 113 } 114}