defmodule NLdoc.Spec do @moduledoc """ This module defines the Ecto schema for the NLdoc spec objects. """ use EnumType @doc """ Returns the version of the NLdoc API spec that the `NLdoc.Spec` library implements. """ @version "6.1.1" @spec version() :: String.t() def version, do: @version alias NLdoc.Spec.{ Asset, BlockQuotation, BooleanDescriptor, DefinitionDetails, DefinitionList, DefinitionTerm, Document, Footnote, FootnoteReference, Heading, Image, IntegerDescriptor, Link, ListItem, NumberDescriptor, ObjectDescriptor, OrderedList, Paragraph, Preformatted, StringDescriptor, Table, TableCell, TableHeader, TableRow, Text, UnorderedList } @type object :: Asset.t() | BlockQuotation.t() | DefinitionDetails.t() | DefinitionList.t() | DefinitionTerm.t() | Document.t() | Footnote.t() | FootnoteReference.t() | Heading.t() | Image.t() | Link.t() | ListItem.t() | OrderedList.t() | Paragraph.t() | Preformatted.t() | Table.t() | TableCell.t() | TableHeader.t() | TableRow.t() | Text.t() | UnorderedList.t() @objects [ Asset, BlockQuotation, DefinitionDetails, DefinitionList, DefinitionTerm, Document, Footnote, FootnoteReference, Heading, Image, Link, ListItem, OrderedList, Paragraph, Preformatted, Table, TableCell, TableHeader, TableRow, Text, UnorderedList ] @type descriptor :: ObjectDescriptor.t() | BooleanDescriptor.t() | StringDescriptor.t() | NumberDescriptor.t() | IntegerDescriptor.t() @descriptors [ ObjectDescriptor, BooleanDescriptor, StringDescriptor, NumberDescriptor, IntegerDescriptor ] def objects, do: @objects def descriptors, do: @descriptors defenum Type do # Resource objects value Asset, "https://spec.nldoc.nl/Resource/Asset" value BlockQuotation, "https://spec.nldoc.nl/Resource/BlockQuotation" value DefinitionDetails, "https://spec.nldoc.nl/Resource/DefinitionDetails" value DefinitionList, "https://spec.nldoc.nl/Resource/DefinitionList" value DefinitionTerm, "https://spec.nldoc.nl/Resource/DefinitionTerm" value Document, "https://spec.nldoc.nl/Resource/Document" value Footnote, "https://spec.nldoc.nl/Resource/Footnote" value FootnoteReference, "https://spec.nldoc.nl/Resource/FootnoteReference" value Heading, "https://spec.nldoc.nl/Resource/Heading" value Image, "https://spec.nldoc.nl/Resource/Image" value Link, "https://spec.nldoc.nl/Resource/Link" value ListItem, "https://spec.nldoc.nl/Resource/ListItem" value OrderedList, "https://spec.nldoc.nl/Resource/OrderedList" value Paragraph, "https://spec.nldoc.nl/Resource/Paragraph" value Preformatted, "https://spec.nldoc.nl/Resource/Preformatted" value Table, "https://spec.nldoc.nl/Resource/Table" value TableCell, "https://spec.nldoc.nl/Resource/TableCell" value TableHeader, "https://spec.nldoc.nl/Resource/TableHeader" value TableRow, "https://spec.nldoc.nl/Resource/TableRow" value Text, "https://spec.nldoc.nl/Resource/Text" value UnorderedList, "https://spec.nldoc.nl/Resource/UnorderedList" # Descriptors value ObjectDescriptor, "https://spec.nldoc.nl/Resource/ObjectDescriptor" value BooleanDescriptor, "https://spec.nldoc.nl/Resource/BooleanDescriptor" value StringDescriptor, "https://spec.nldoc.nl/Resource/StringDescriptor" value NumberDescriptor, "https://spec.nldoc.nl/Resource/NumberDescriptor" value IntegerDescriptor, "https://spec.nldoc.nl/Resource/IntegerDescriptor" # Validation value Validation, "https://spec.validation.nldoc.nl/Resource/Validation" end @type blockquote_child() :: DefinitionList.t() | Heading.t() | Image.t() | OrderedList.t() | Paragraph.t() | Preformatted.t() | Table.t() | UnorderedList.t() def blockquote_children do [ DefinitionList, Heading, Image, OrderedList, Paragraph, Preformatted, Table, UnorderedList ] end @type document_child() :: BlockQuotation.t() | blockquote_child() def document_children do [BlockQuotation | blockquote_children()] end @type paragraph_child() :: Image.t() | Link.t() | Text.t() | FootnoteReference.t() def paragraph_children do [Image, Link, Text, FootnoteReference] end @type text_style() :: :bold | :italic | :underline | :strikethrough | :code | :superscript | :subscript | :mark @spec text_styling() :: [text_style()] def text_styling do [:bold, :italic, :underline, :strikethrough, :code, :superscript, :subscript, :mark] end @doc """ Recursively find the first spec object to match the given predicate, or return the given default value. This function performs a depth-first search through the children of a spec object or list of spec objects, and returns the first spec object for which the predicate returns true. ## Examples iex> alias NLdoc.Spec.{Paragraph, Text} iex> NLdoc.Spec.find_recursive( ...> [%Text{text: "1"}, %Text{text: "2"}], ...> fn object -> object.text === "2" end ...> ) %Text{text: "2"} iex> NLdoc.Spec.find_recursive( ...> [%Paragraph{children: [%Text{text: "1"}, %Text{text: "2"}]}, %Paragraph{children: [%Text{text: "3"}, %Text{text: "4"}]}], ...> fn ...> %Text{text: text} -> text |> String.to_integer() |> rem(2) === 0 ...> _ -> false ...> end ...> ) %Text{text: "2"} iex> NLdoc.Spec.find_recursive([], fn _ -> true end) nil iex> NLdoc.Spec.find_recursive([], fn _ -> true end, "test") "test" iex> NLdoc.Spec.find_recursive([%Text{text: "1"}, %Text{text: "2"}], fn _ -> false end) nil iex> NLdoc.Spec.find_recursive([%Text{text: "1"}, %Text{text: "2"}], fn _ -> false end, "default") "default" """ @spec find_recursive(object() | [object()], (object() -> boolean())) :: object() | nil def find_recursive([], _predicate), do: nil def find_recursive([head | tail], predicate) when is_list(tail), do: find_recursive(head, predicate) || find_recursive(tail, predicate) def find_recursive(%type{} = object, predicate) when type in @objects do if predicate.(object) do object else object |> Map.get(:children, []) |> find_recursive(predicate) end end @spec find_recursive([object()], (object() -> boolean()), default) :: object() | default when default: var def find_recursive(elements, predicate, default), do: find_recursive(elements, predicate) || default end