// ========================================== // DOMAIN: ДОКУМЕНТ И КОНТРАКТ ПРОФИЛЯ // ========================================== #let fail(scope, message) = panic("Scientia / " + scope + ": " + message) #let ensure-dictionary(value, scope) = { if type(value) != dictionary { fail(scope, "ожидался dictionary, получено " + repr(type(value))) } value } #let merge-known(defaults, overrides, scope: "options", allow-extra: false) = { let defaults = ensure-dictionary(defaults, scope + ".defaults") let overrides = ensure-dictionary(overrides, scope) let result = defaults for (key, value) in overrides { if not allow-extra and key not in defaults { fail(scope, "неизвестный параметр " + repr(key)) } result.insert(key, value) } result } #let render-options(options: (:)) = { let resolved = merge-known( ( mode: "final", watermark: none, media-policy: "placeholder", diagnostics: true, ), options, scope: "render-options", ) if resolved.mode not in ("final", "draft", "clean-copy") { fail("render-options.mode", "допустимы final, draft, clean-copy") } if resolved.at("media-policy") not in ("hide", "placeholder", "reserve-space") { fail("render-options.media-policy", "допустимы hide, placeholder, reserve-space") } resolved } #let document-profile( id, render, metadata: (:), normalize: value => value, validate: value => value, ) = { if type(id) != str or id.trim() == "" { fail("document-profile.id", "нужна непустая строка") } if type(metadata) != dictionary { fail("document-profile.metadata", "ожидался dictionary") } if type(normalize) != function { fail("document-profile.normalize", "ожидалась function") } if type(validate) != function { fail("document-profile.validate", "ожидалась function") } if type(render) != function { fail("document-profile.render", "ожидалась function") } ( kind: "document-profile", id: id, metadata: metadata, normalize: normalize, validate: validate, render: render, ) } #let validate-profile-contract(profile) = { let profile = ensure-dictionary(profile, "profile") if profile.at("kind", default: none) != "document-profile" { fail("profile.kind", "ожидался document-profile") } for key in ("id", "metadata", "normalize", "validate", "render") { if key not in profile { fail("profile", "отсутствует обязательное поле " + repr(key)) } } if type(profile.normalize) != function or type(profile.validate) != function or type(profile.render) != function { fail("profile", "normalize, validate и render должны быть functions") } profile } #let document-context( company, profile, metadata, options, parties: (), attachments: (), bibliographies: (), ) = { ( kind: "document-context", company: company, profile-id: profile.id, metadata: ensure-dictionary(metadata, "document-context.metadata"), options: render-options(options: options), parties: parties, attachments: attachments, bibliographies: bibliographies, ) }