Ir para o conteúdo

Escrever recipes

Uma recipe é um pequeno ficheiro TOML que ensina o motor genérico sobre um dialecto XML. O motor mantém-se igual; tudo o que é específico do dialecto vive aqui.

Deixa um LLM rascunhar

Podes gerar uma primeira recipe a partir de uma amostra do teu XML — ver Gerar uma recipe com um LLM.

Anatomia

name = "controlm"

[defaults]
unit = "SMART_FOLDER"           # a unidade de comparação (default: filhos da raiz)
unordered = true                # filhos casados por chave, não por ordem
ignore_attrs = [                # atributos voláteis nunca geram diff
    "VERSION", "JOBISN", "CREATION_TIME", "LAST_UPLOAD", "..."
]

[elements.JOB]
key = ["@JOBNAME"]

[elements.OUTCOND]
key = ["@NAME"]                 # ODATE / SIGN são comparados como atributos

[elements.ON]                   # sem chave clara → sintetiza-se uma
key = ["@CODE", "*kinds"]
inline = true                   # trata os filhos (DOACTION, …) como pseudo-atributos

A mini-linguagem da chave

Uma key é uma lista de tokens, juntos por |. A primeira combinação não-vazia identifica o elemento entre os irmãos.

Token Significado
@ATTR valor do atributo ATTR
#text o texto do próprio elemento
*tag o nome da tag (usar para singletons comparados pelo texto)
child:TAG@ATTR um atributo de um filho
child:TAG#text o texto de um filho (ex. <loc> do sitemap)
*kinds resumo dos tipos de filhos / ações DOACTION (para elementos sem chave como <ON>)

Se uma tag não tiver entrada, o motor recorre a @NAME, depois #text, depois um composto de todos os atributos.

Exemplos

# Uma condição com chave NAME; os outros atributos são comparados
[elements.INCOND]
key = ["@NAME"]                 # ODATE, AND_OR ficam comparáveis

# Um <url> de sitemap identificado pelo texto do filho <loc>
[elements.url]
key = ["child:loc#text"]

# Singletons comparados pelo texto (identidade = a própria tag)
[elements.lastmod]
key = ["*tag"]

# Um <ON STMT="*" CODE="NOTOK"> sem chave estável → CODE + ações
[elements.ON]
key = ["@CODE", "*kinds"]       # ex. "NOTOK|RERUN"
inline = true

Elementos inline

Alguns elementos (como o <ON> do Control-M) levam o significado nos filhos (DOACTION, DOMAIL, DOOUTPUT). Marcá-los inline = true dobra esses filhos em pseudo-atributos, por isso uma mudança como “a ação RERUN foi removida” aparece numa só linha em vez de uma sub-secção encaixada.

Recipes embutidas

Exports BMC Control-M: DEFTABLE → SMART_FOLDER → JOB → INCOND / OUTCOND / QUANTITATIVE / CONTROL / ON. Unit = SMART_FOLDER, com uma lista ignore_attrs ampla para metadados de versão/criação.

pom.xml do Maven: drift de dependências e plugins. Uma <dependency> / <plugin> é identificada pelas coordenadas (groupId:artifactId[:type:classifier]), por isso o diff reporta mudanças de versão/scope e entradas adicionadas/removidas em <dependencies>, <dependencyManagement> e <build>, independente da ordem. (Sem unit: as secções do POM são as unidades — evita que a mesma coordenada em <dependencies> e <dependencyManagement> colida, e deixa o add/remove aparecer como presença.)

Relatórios JUnit / xUnit (Surefire, Gradle, pytest, Jest, …). Unit = testsuite (por @name); um <testcase> é identificado por classname + name e marcado inline, por isso as transições pass↔fail↔skip e os testes adicionados/removidos aparecem numa linha cada — enquanto time / timestamp / hostname e os contadores são ignorados.

sitemap.xml: unit = url, identificado pelo texto do <loc>; <lastmod> / <priority> / <changefreq> comparados pelo texto.

Sem conhecimento de dialecto: as unidades são os filhos da raiz; a identidade recorre a @NAME / #text. É a omissão quando --recipe não é passado.

Usar uma recipe própria

Guarda o teu .toml onde quiseres e passa o caminho:

xmldiffreport ./data --recipe ./meu-dialecto.toml -o report.md

Para a contribuir como embutida, coloca-a em src/xmldiffreport/recipes/ e acrescenta um exemplo sintético + teste (ver Contribuir).