// @generated by idiolect-codegen. do not edit.
// source: dev.idiolect.vocab
//! A community-published vocabulary. Two compatible shapes are supported: (1) the legacy single-relation tree (`actions` + `top` + `world`), where every entry declares its direct subsumers and the world pins the inference discipline; and (2) the typed multi-relation knowledge graph (`nodes` + `edges`), where every node is first-class and edges carry a relation slug. Authors choose either; consumers normalize the legacy tree to the graph form at access time by lifting each `actionEntry` to a node and each `parents` entry to a `subsumed_by` edge. New vocabularies should prefer the graph shape; the tree stays valid for backward compatibility and remains the right shape for pure subsumption hierarchies. Modeled after `pub.chive.graph.{node,edge}` so cross-vocabulary translation, SKOS-style `broader_than`/`narrower_than` mappings, and external-id alignment (Wikidata, ROR, ORCID, ...) are first-class.
#![allow(
missing_docs,
clippy::doc_markdown,
clippy::struct_excessive_bools,
clippy::derive_partial_eq_without_eq,
clippy::large_enum_variant
)]
use serde::{Deserialize, Serialize};
/// A vocabulary record. Carries a name, world discipline, optional default relation, and either a tree-shaped `actions` set, a graph-shaped `nodes`+`edges` pair, or both. When both are present, consumers union them after lifting the tree to graph form.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Vocab {
/// Legacy tree shape: actions with their direct subsumers. Equivalent to a graph in which every action is a node and every parent is a `subsumed_by` edge. Consumers should normalize this to the graph form when reading.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub actions: Option<Vec<ActionEntry>>,
/// Optional pointer to the relation-kind node that consumers should treat as canonical when no relation is specified (typically the `subsumed_by` node). Lets graph-shaped vocabularies recover tree-style queries without a hard-coded relation.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default_relation: Option<idiolect_records::AtUri>,
/// Narrative description of the vocabulary's intended scope and conventions. Not consumed by machine matchers.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
/// Graph shape: typed edges between nodes. Each edge carries a relation slug (e.g. `subsumed_by`, `equivalent_to`, `broader_than`, `narrower_than`).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub edges: Option<Vec<VocabEdge>>,
/// Human-readable vocabulary name, e.g. 'pedagogical-use-v1'.
pub name: String,
/// Graph shape: typed nodes. Each node is a concept, an instance, a relation kind, or any other subkind. Relation-kind nodes carry `relationMetadata` (symmetric/transitive/reflexive/functional/inverseOf).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub nodes: Option<Vec<VocabNode>>,
pub occurred_at: idiolect_records::Datetime,
/// Prior vocabulary this one replaces. Consumers that pinned to `supersedes` may continue to resolve against it.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub supersedes: Option<idiolect_records::AtUri>,
/// Identifier of the vocabulary's top action under `subsumed_by`. Required when `actions` is populated and `world=closed-with-default`. Recoverable from the graph form as the unique node with no outbound `subsumed_by` edge.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub top: Option<String>,
/// Default subsumption world discipline (applies to the `subsumed_by` relation when not overridden per-relation). 'closed-with-default' — every undeclared id is subsumed by `top` and nothing else. 'open' — undeclared ids are incomparable to every declared id. 'hierarchy-closed' — only the declared edges exist. Closed enum: meta-policy on the inference engine, not a domain term.
pub world: VocabWorld,
}
impl crate::Record for Vocab {
const NSID: &'static str = "dev.idiolect.vocab";
}
/// Legacy tree-shape entry: one action with its direct subsumers. Equivalent to a `vocabNode` plus a `subsumed_by` edge per parent. Retained for backwards compatibility.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ActionEntry {
/// Identifier of the attitudinal composition this action is an instance of. Consumers dispatch on both the action's position in the hierarchy and its attitudinal shape. Omit to inherit the vocabulary's default stance; consumers treat the class as asserted_use when absent.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub class: Option<String>,
/// Optional human-readable description. Not matched on; annotation for readers.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
/// Stable action identifier (e.g. 'train_model', 'annotate').
pub id: String,
/// Direct subsumers. The action is transitively subsumed by every ancestor in the hierarchy. Empty for `top`.
pub parents: Vec<String>,
}
/// Optional edge-specific metadata. Consumers ignore unknown fields.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EdgeMetadata {
/// Confidence score scaled by 1000 (0.0-1.0). Useful for inferred or weakly-attested edges.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub confidence: Option<i64>,
/// Temporal end of the relationship's validity.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub end_date: Option<idiolect_records::Datetime>,
/// Free-form source attestation (citation, pointer to derivation provenance).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
/// Temporal start of the relationship's validity.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub start_date: Option<idiolect_records::Datetime>,
}
/// Mapping from a vocab node to its identifier in an external knowledge base.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalId {
/// Identifier value within the named system (e.g. a Wikidata QID, a ROR id).
pub identifier: String,
/// Open-enum SKOS-style match strength. Resolved against `matchTypeVocab` when present.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub match_type: Option<ExternalIdMatchType>,
/// Vocabulary the `matchType` slug resolves against.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub match_type_vocab: Option<crate::generated::dev::idiolect::defs::VocabRef>,
/// Open-enum identifier-system slug. Resolved against `systemVocab` when present.
pub system: ExternalIdSystem,
/// Vocabulary the `system` slug resolves against.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub system_vocab: Option<crate::generated::dev::idiolect::defs::VocabRef>,
/// Optional full URI for the external identifier.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub uri: Option<idiolect_records::Uri>,
}
/// Algebraic metadata for a relation-kind node. Consumers reasoning over edges of this relation can rely on these properties to lift queries: e.g., a transitive relation supports closure walks; a symmetric relation lets queries traverse in either direction. The full OWL Lite property-characteristic set is supported (symmetric, asymmetric, transitive, reflexive, irreflexive, functional, inverseFunctional, inverseOf).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RelationMetadata {
/// A rel B implies NOT (B rel A). Declarative; consumers may use this to validate that no edge contradicts the asymmetry. Mutually exclusive with `symmetric` (declaring both is a config error).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub asymmetric: Option<bool>,
/// Each source has at most one target under this relation. Validated by `VocabGraph::validate`; sources with multiple outbound edges of a functional relation surface as a violation.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub functional: Option<bool>,
/// Each target has at most one source under this relation. Validated by `VocabGraph::validate`; useful for declaring identifier-like relations (e.g. `has_isbn`).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub inverse_functional: Option<bool>,
/// Slug of the inverse relation (e.g. `narrower_than` for `broader_than`). Edges in this relation imply mirror-inverse edges in the named relation.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub inverse_of: Option<String>,
/// NOT (A rel A) for any A. Declarative; consumers may use this to validate that no self-loop edge exists. Mutually exclusive with `reflexive`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub irreflexive: Option<bool>,
/// A rel A holds for every A. Reflected in the `reflexive` argument of walks.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reflexive: Option<bool>,
/// A rel B implies B rel A. Walks traverse outbound and inbound edges as one set.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub symmetric: Option<bool>,
/// A rel B and B rel C imply A rel C. Walks compute the transitive closure.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub transitive: Option<bool>,
/// Per-relation world override. Closed enum: meta-policy.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub world: Option<RelationMetadataWorld>,
}
/// A typed directed edge between two nodes. The relation is identified both by slug (`relationSlug`, for indexing) and optionally by AT-URI pointer to a relation-kind node.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VocabEdge {
/// Optional edge-specific metadata (confidence, temporal validity, source attestation).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub metadata: Option<EdgeMetadata>,
/// Open-enum relation-type slug. The canonical default vocabulary seeds the values shown; communities extend by referencing `relationVocab`.
pub relation_slug: VocabEdgeRelationSlug,
/// Optional pointer to the relation-kind node (typically a node with kind=relation) backing this edge.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub relation_uri: Option<idiolect_records::AtUri>,
/// Vocabulary the `relationSlug` resolves against.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub relation_vocab: Option<crate::generated::dev::idiolect::defs::VocabRef>,
/// Source node id within the same vocabulary, or an AT-URI for a cross-vocabulary edge.
pub source: String,
/// Target node id within the same vocabulary, or an AT-URI for a cross-vocabulary edge.
pub target: String,
/// Optional edge weight scaled by 1000 for the 0.0-1.0 range. Convention follows `pub.chive.graph.edge#weight`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub weight: Option<i64>,
}
/// A typed node in a knowledge-graph-shaped vocabulary. Mirrors `pub.chive.graph.node`: nodes carry stable ids, optional human-readable labels, optional external-id mappings, and a status. Relation-kind nodes additionally carry `relationMetadata`.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VocabNode {
/// Synonyms, translations, or alternate spellings (SKOS `altLabel`).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub alternate_labels: Option<Vec<String>>,
/// SKOS `changeNote`: a fine-grained record of a single change.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub change_note: Option<String>,
/// AT-URI of the node that supersedes this one, when status=deprecated.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub deprecated_by: Option<idiolect_records::AtUri>,
/// SKOS `definition`: a complete explanation of what the concept means.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
/// SKOS `editorialNote`: management-level annotations not visible to end users.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub editorial_note: Option<String>,
/// SKOS `example`: a concrete instance or usage example.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub example: Option<String>,
/// External identifier mappings into non-ATProto knowledge bases (Wikidata, ROR, ORCID, ...). Enables cross-system translation.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub external_ids: Option<Vec<ExternalId>>,
/// SKOS `hiddenLabel`: searchable but not displayed. Useful for misspellings, deprecated forms, lookup synonyms.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hidden_labels: Option<Vec<String>>,
/// SKOS `historyNote`: significant changes in the concept's history.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub history_note: Option<String>,
/// Stable node identifier (slug). Used as the source/target of edges within the same vocabulary.
pub id: String,
/// Open-enum slug naming the node kind. `relation` means this node represents a relation type; `concept` is a domain category; `instance` is an individual; `type` is a metaclass; `collection` is a SKOS-style grouping of concepts (members declared via `member_of` edges). Consumers dispatch on this; resolved against `kindVocab` when present.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kind: Option<VocabNodeKind>,
/// Vocabulary the `kind` slug resolves against. Omit to use the canonical idiolect default.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kind_vocab: Option<crate::generated::dev::idiolect::defs::VocabRef>,
/// Primary human-readable label.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
/// SKOS `notation`: a non-text identifier (e.g. a Dewey decimal code, an ISO designator). Distinct from `id`: notation is for display and indexing in legacy classification systems; `id` is the slug used internally.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub notation: Option<String>,
/// Algebraic metadata, populated only when this node represents a relation type (kind=relation).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub relation_metadata: Option<RelationMetadata>,
/// SKOS `scopeNote`: guidance on how the concept should be used. Distinct from `description` (definition) — scopeNote covers application boundaries, common pitfalls, when to prefer a sibling concept.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scope_note: Option<String>,
/// Open-enum lifecycle status. Resolved against `statusVocab` when present.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status: Option<VocabNodeStatus>,
/// Vocabulary the `status` slug resolves against.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status_vocab: Option<crate::generated::dev::idiolect::defs::VocabRef>,
/// Optional pointer to another node identifying this node's subkind. Enables typed vocabularies (e.g., a node tagged as `attitudinal-class`).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub subkind_uri: Option<idiolect_records::AtUri>,
}
/// ExternalIdMatchType. Open-enum slug; known values are kebab-cased; community-extended values pass through as `Other(String)`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ExternalIdMatchType {
Exact,
Close,
Broader,
Narrower,
Related,
/// Community-extended slug not present in the lexicon's
/// `knownValues`. Resolves through the sibling
/// `*Vocab` field on the containing record.
Other(String),
}
impl ExternalIdMatchType {
/// Wire-form slug for this value. Known variants render
/// kebab-case; the fallback variant passes through verbatim.
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::Exact => "exact",
Self::Close => "close",
Self::Broader => "broader",
Self::Narrower => "narrower",
Self::Related => "related",
Self::Other(s) => s.as_str(),
}
}
/// Whether this slug is subsumed by `ancestor` under the
/// `subsumed_by` relation in the supplied vocab. Reflexive:
/// every slug is subsumed by itself.
#[must_use]
pub fn is_subsumed_by(
&self,
vocab: &idiolect_records::vocab::VocabGraph,
ancestor: &str,
) -> bool {
vocab.is_subsumed_by(self.as_str(), ancestor)
}
/// Whether this slug satisfies a requirement of `target`
/// under the named `relation` in the supplied vocab.
/// Generalises `is_subsumed_by` to any directed relation
/// (e.g. `stronger_than`, `provides_at_least`,
/// `equivalent_to`). Reflexive: a slug satisfies itself.
#[must_use]
pub fn satisfies(
&self,
vocab: &idiolect_records::vocab::VocabGraph,
relation: &str,
target: &str,
) -> bool {
if self.as_str() == target {
return true;
}
vocab
.walk_relation(self.as_str(), relation, false)
.iter()
.any(|n| n == target)
}
/// Translate this slug across vocabularies via
/// `equivalent_to` edges. Returns the translated slug as
/// a target enum value when a translation exists, `None`
/// when no path is found (callers fall back to passing
/// the slug through verbatim, which is wire-compatible).
#[must_use]
pub fn translate_to<T: From<String>>(
&self,
src_vocab_uri: &str,
tgt_vocab_uri: &str,
registry: &idiolect_records::vocab::VocabRegistry,
) -> Option<T> {
registry
.translate(src_vocab_uri, tgt_vocab_uri, self.as_str())
.map(T::from)
}
}
impl From<String> for ExternalIdMatchType {
fn from(s: String) -> Self {
match s.as_str() {
"exact" => Self::Exact,
"close" => Self::Close,
"broader" => Self::Broader,
"narrower" => Self::Narrower,
"related" => Self::Related,
_ => Self::Other(s),
}
}
}
impl From<&str> for ExternalIdMatchType {
fn from(s: &str) -> Self {
match s {
"exact" => Self::Exact,
"close" => Self::Close,
"broader" => Self::Broader,
"narrower" => Self::Narrower,
"related" => Self::Related,
_ => Self::Other(s.to_owned()),
}
}
}
impl serde::Serialize for ExternalIdMatchType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de> serde::Deserialize<'de> for ExternalIdMatchType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(Self::from(s))
}
}
/// ExternalIdSystem. Open-enum slug; known values are kebab-cased; community-extended values pass through as `Other(String)`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ExternalIdSystem {
Wikidata,
Ror,
Orcid,
Isni,
Viaf,
Lcsh,
Fast,
Skos,
DublinCore,
SchemaOrg,
Mesh,
Aat,
/// Community-extended slug not present in the lexicon's
/// `knownValues`. Resolves through the sibling
/// `*Vocab` field on the containing record.
Other(String),
}
impl ExternalIdSystem {
/// Wire-form slug for this value. Known variants render
/// kebab-case; the fallback variant passes through verbatim.
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::Wikidata => "wikidata",
Self::Ror => "ror",
Self::Orcid => "orcid",
Self::Isni => "isni",
Self::Viaf => "viaf",
Self::Lcsh => "lcsh",
Self::Fast => "fast",
Self::Skos => "skos",
Self::DublinCore => "dublin-core",
Self::SchemaOrg => "schema-org",
Self::Mesh => "mesh",
Self::Aat => "aat",
Self::Other(s) => s.as_str(),
}
}
/// Whether this slug is subsumed by `ancestor` under the
/// `subsumed_by` relation in the supplied vocab. Reflexive:
/// every slug is subsumed by itself.
#[must_use]
pub fn is_subsumed_by(
&self,
vocab: &idiolect_records::vocab::VocabGraph,
ancestor: &str,
) -> bool {
vocab.is_subsumed_by(self.as_str(), ancestor)
}
/// Whether this slug satisfies a requirement of `target`
/// under the named `relation` in the supplied vocab.
/// Generalises `is_subsumed_by` to any directed relation
/// (e.g. `stronger_than`, `provides_at_least`,
/// `equivalent_to`). Reflexive: a slug satisfies itself.
#[must_use]
pub fn satisfies(
&self,
vocab: &idiolect_records::vocab::VocabGraph,
relation: &str,
target: &str,
) -> bool {
if self.as_str() == target {
return true;
}
vocab
.walk_relation(self.as_str(), relation, false)
.iter()
.any(|n| n == target)
}
/// Translate this slug across vocabularies via
/// `equivalent_to` edges. Returns the translated slug as
/// a target enum value when a translation exists, `None`
/// when no path is found (callers fall back to passing
/// the slug through verbatim, which is wire-compatible).
#[must_use]
pub fn translate_to<T: From<String>>(
&self,
src_vocab_uri: &str,
tgt_vocab_uri: &str,
registry: &idiolect_records::vocab::VocabRegistry,
) -> Option<T> {
registry
.translate(src_vocab_uri, tgt_vocab_uri, self.as_str())
.map(T::from)
}
}
impl From<String> for ExternalIdSystem {
fn from(s: String) -> Self {
match s.as_str() {
"wikidata" => Self::Wikidata,
"ror" => Self::Ror,
"orcid" => Self::Orcid,
"isni" => Self::Isni,
"viaf" => Self::Viaf,
"lcsh" => Self::Lcsh,
"fast" => Self::Fast,
"skos" => Self::Skos,
"dublin-core" => Self::DublinCore,
"schema-org" => Self::SchemaOrg,
"mesh" => Self::Mesh,
"aat" => Self::Aat,
_ => Self::Other(s),
}
}
}
impl From<&str> for ExternalIdSystem {
fn from(s: &str) -> Self {
match s {
"wikidata" => Self::Wikidata,
"ror" => Self::Ror,
"orcid" => Self::Orcid,
"isni" => Self::Isni,
"viaf" => Self::Viaf,
"lcsh" => Self::Lcsh,
"fast" => Self::Fast,
"skos" => Self::Skos,
"dublin-core" => Self::DublinCore,
"schema-org" => Self::SchemaOrg,
"mesh" => Self::Mesh,
"aat" => Self::Aat,
_ => Self::Other(s.to_owned()),
}
}
}
impl serde::Serialize for ExternalIdSystem {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de> serde::Deserialize<'de> for ExternalIdSystem {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(Self::from(s))
}
}
/// RelationMetadataWorld.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum RelationMetadataWorld {
ClosedWithDefault,
Open,
HierarchyClosed,
}
/// VocabEdgeRelationSlug. Open-enum slug; known values are kebab-cased; community-extended values pass through as `Other(String)`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum VocabEdgeRelationSlug {
SubsumedBy,
BroaderThan,
NarrowerThan,
EquivalentTo,
PolarOppositeOf,
RelatedTo,
InstanceOf,
PartOf,
MemberOf,
/// Community-extended slug not present in the lexicon's
/// `knownValues`. Resolves through the sibling
/// `*Vocab` field on the containing record.
Other(String),
}
impl VocabEdgeRelationSlug {
/// Wire-form slug for this value. Known variants render
/// kebab-case; the fallback variant passes through verbatim.
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::SubsumedBy => "subsumed_by",
Self::BroaderThan => "broader_than",
Self::NarrowerThan => "narrower_than",
Self::EquivalentTo => "equivalent_to",
Self::PolarOppositeOf => "polar_opposite_of",
Self::RelatedTo => "related_to",
Self::InstanceOf => "instance_of",
Self::PartOf => "part_of",
Self::MemberOf => "member_of",
Self::Other(s) => s.as_str(),
}
}
/// Whether this slug is subsumed by `ancestor` under the
/// `subsumed_by` relation in the supplied vocab. Reflexive:
/// every slug is subsumed by itself.
#[must_use]
pub fn is_subsumed_by(
&self,
vocab: &idiolect_records::vocab::VocabGraph,
ancestor: &str,
) -> bool {
vocab.is_subsumed_by(self.as_str(), ancestor)
}
/// Whether this slug satisfies a requirement of `target`
/// under the named `relation` in the supplied vocab.
/// Generalises `is_subsumed_by` to any directed relation
/// (e.g. `stronger_than`, `provides_at_least`,
/// `equivalent_to`). Reflexive: a slug satisfies itself.
#[must_use]
pub fn satisfies(
&self,
vocab: &idiolect_records::vocab::VocabGraph,
relation: &str,
target: &str,
) -> bool {
if self.as_str() == target {
return true;
}
vocab
.walk_relation(self.as_str(), relation, false)
.iter()
.any(|n| n == target)
}
/// Translate this slug across vocabularies via
/// `equivalent_to` edges. Returns the translated slug as
/// a target enum value when a translation exists, `None`
/// when no path is found (callers fall back to passing
/// the slug through verbatim, which is wire-compatible).
#[must_use]
pub fn translate_to<T: From<String>>(
&self,
src_vocab_uri: &str,
tgt_vocab_uri: &str,
registry: &idiolect_records::vocab::VocabRegistry,
) -> Option<T> {
registry
.translate(src_vocab_uri, tgt_vocab_uri, self.as_str())
.map(T::from)
}
}
impl From<String> for VocabEdgeRelationSlug {
fn from(s: String) -> Self {
match s.as_str() {
"subsumed_by" => Self::SubsumedBy,
"broader_than" => Self::BroaderThan,
"narrower_than" => Self::NarrowerThan,
"equivalent_to" => Self::EquivalentTo,
"polar_opposite_of" => Self::PolarOppositeOf,
"related_to" => Self::RelatedTo,
"instance_of" => Self::InstanceOf,
"part_of" => Self::PartOf,
"member_of" => Self::MemberOf,
_ => Self::Other(s),
}
}
}
impl From<&str> for VocabEdgeRelationSlug {
fn from(s: &str) -> Self {
match s {
"subsumed_by" => Self::SubsumedBy,
"broader_than" => Self::BroaderThan,
"narrower_than" => Self::NarrowerThan,
"equivalent_to" => Self::EquivalentTo,
"polar_opposite_of" => Self::PolarOppositeOf,
"related_to" => Self::RelatedTo,
"instance_of" => Self::InstanceOf,
"part_of" => Self::PartOf,
"member_of" => Self::MemberOf,
_ => Self::Other(s.to_owned()),
}
}
}
impl serde::Serialize for VocabEdgeRelationSlug {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de> serde::Deserialize<'de> for VocabEdgeRelationSlug {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(Self::from(s))
}
}
/// VocabNodeKind. Open-enum slug; known values are kebab-cased; community-extended values pass through as `Other(String)`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum VocabNodeKind {
Concept,
Relation,
Instance,
Type,
Collection,
/// Community-extended slug not present in the lexicon's
/// `knownValues`. Resolves through the sibling
/// `*Vocab` field on the containing record.
Other(String),
}
impl VocabNodeKind {
/// Wire-form slug for this value. Known variants render
/// kebab-case; the fallback variant passes through verbatim.
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::Concept => "concept",
Self::Relation => "relation",
Self::Instance => "instance",
Self::Type => "type",
Self::Collection => "collection",
Self::Other(s) => s.as_str(),
}
}
/// Whether this slug is subsumed by `ancestor` under the
/// `subsumed_by` relation in the supplied vocab. Reflexive:
/// every slug is subsumed by itself.
#[must_use]
pub fn is_subsumed_by(
&self,
vocab: &idiolect_records::vocab::VocabGraph,
ancestor: &str,
) -> bool {
vocab.is_subsumed_by(self.as_str(), ancestor)
}
/// Whether this slug satisfies a requirement of `target`
/// under the named `relation` in the supplied vocab.
/// Generalises `is_subsumed_by` to any directed relation
/// (e.g. `stronger_than`, `provides_at_least`,
/// `equivalent_to`). Reflexive: a slug satisfies itself.
#[must_use]
pub fn satisfies(
&self,
vocab: &idiolect_records::vocab::VocabGraph,
relation: &str,
target: &str,
) -> bool {
if self.as_str() == target {
return true;
}
vocab
.walk_relation(self.as_str(), relation, false)
.iter()
.any(|n| n == target)
}
/// Translate this slug across vocabularies via
/// `equivalent_to` edges. Returns the translated slug as
/// a target enum value when a translation exists, `None`
/// when no path is found (callers fall back to passing
/// the slug through verbatim, which is wire-compatible).
#[must_use]
pub fn translate_to<T: From<String>>(
&self,
src_vocab_uri: &str,
tgt_vocab_uri: &str,
registry: &idiolect_records::vocab::VocabRegistry,
) -> Option<T> {
registry
.translate(src_vocab_uri, tgt_vocab_uri, self.as_str())
.map(T::from)
}
}
impl From<String> for VocabNodeKind {
fn from(s: String) -> Self {
match s.as_str() {
"concept" => Self::Concept,
"relation" => Self::Relation,
"instance" => Self::Instance,
"type" => Self::Type,
"collection" => Self::Collection,
_ => Self::Other(s),
}
}
}
impl From<&str> for VocabNodeKind {
fn from(s: &str) -> Self {
match s {
"concept" => Self::Concept,
"relation" => Self::Relation,
"instance" => Self::Instance,
"type" => Self::Type,
"collection" => Self::Collection,
_ => Self::Other(s.to_owned()),
}
}
}
impl serde::Serialize for VocabNodeKind {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de> serde::Deserialize<'de> for VocabNodeKind {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(Self::from(s))
}
}
/// VocabNodeStatus. Open-enum slug; known values are kebab-cased; community-extended values pass through as `Other(String)`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum VocabNodeStatus {
Proposed,
Provisional,
Established,
Deprecated,
/// Community-extended slug not present in the lexicon's
/// `knownValues`. Resolves through the sibling
/// `*Vocab` field on the containing record.
Other(String),
}
impl VocabNodeStatus {
/// Wire-form slug for this value. Known variants render
/// kebab-case; the fallback variant passes through verbatim.
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::Proposed => "proposed",
Self::Provisional => "provisional",
Self::Established => "established",
Self::Deprecated => "deprecated",
Self::Other(s) => s.as_str(),
}
}
/// Whether this slug is subsumed by `ancestor` under the
/// `subsumed_by` relation in the supplied vocab. Reflexive:
/// every slug is subsumed by itself.
#[must_use]
pub fn is_subsumed_by(
&self,
vocab: &idiolect_records::vocab::VocabGraph,
ancestor: &str,
) -> bool {
vocab.is_subsumed_by(self.as_str(), ancestor)
}
/// Whether this slug satisfies a requirement of `target`
/// under the named `relation` in the supplied vocab.
/// Generalises `is_subsumed_by` to any directed relation
/// (e.g. `stronger_than`, `provides_at_least`,
/// `equivalent_to`). Reflexive: a slug satisfies itself.
#[must_use]
pub fn satisfies(
&self,
vocab: &idiolect_records::vocab::VocabGraph,
relation: &str,
target: &str,
) -> bool {
if self.as_str() == target {
return true;
}
vocab
.walk_relation(self.as_str(), relation, false)
.iter()
.any(|n| n == target)
}
/// Translate this slug across vocabularies via
/// `equivalent_to` edges. Returns the translated slug as
/// a target enum value when a translation exists, `None`
/// when no path is found (callers fall back to passing
/// the slug through verbatim, which is wire-compatible).
#[must_use]
pub fn translate_to<T: From<String>>(
&self,
src_vocab_uri: &str,
tgt_vocab_uri: &str,
registry: &idiolect_records::vocab::VocabRegistry,
) -> Option<T> {
registry
.translate(src_vocab_uri, tgt_vocab_uri, self.as_str())
.map(T::from)
}
}
impl From<String> for VocabNodeStatus {
fn from(s: String) -> Self {
match s.as_str() {
"proposed" => Self::Proposed,
"provisional" => Self::Provisional,
"established" => Self::Established,
"deprecated" => Self::Deprecated,
_ => Self::Other(s),
}
}
}
impl From<&str> for VocabNodeStatus {
fn from(s: &str) -> Self {
match s {
"proposed" => Self::Proposed,
"provisional" => Self::Provisional,
"established" => Self::Established,
"deprecated" => Self::Deprecated,
_ => Self::Other(s.to_owned()),
}
}
}
impl serde::Serialize for VocabNodeStatus {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de> serde::Deserialize<'de> for VocabNodeStatus {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(Self::from(s))
}
}
/// VocabWorld.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum VocabWorld {
ClosedWithDefault,
Open,
HierarchyClosed,
}