Skip to main content

xbrl_rs/instance/
context.rs

1//! XBRL context definitions
2//!
3//! Contexts define the reporting entity and time period for facts.
4
5use crate::ExpandedName;
6use std::{borrow::Borrow, collections::HashMap, fmt, ops::Deref};
7
8/// Type-safe identifier for an XBRL context (the `id` attribute on
9/// `<xbrli:context>` elements).
10#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
11pub struct ContextId(String);
12
13impl ContextId {
14    pub fn as_str(&self) -> &str {
15        &self.0
16    }
17}
18
19impl From<String> for ContextId {
20    fn from(value: String) -> Self {
21        Self(value)
22    }
23}
24
25impl From<&str> for ContextId {
26    fn from(value: &str) -> Self {
27        Self(value.to_owned())
28    }
29}
30
31impl Deref for ContextId {
32    type Target = str;
33    fn deref(&self) -> &Self::Target {
34        self.as_str()
35    }
36}
37
38impl AsRef<str> for ContextId {
39    fn as_ref(&self) -> &str {
40        self.as_str()
41    }
42}
43
44impl Borrow<str> for ContextId {
45    fn borrow(&self) -> &str {
46        self.as_str()
47    }
48}
49
50impl fmt::Display for ContextId {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        f.write_str(self.as_str())
53    }
54}
55
56/// Identifies the reporting entity
57#[derive(Debug, Clone, PartialEq)]
58pub struct EntityIdentifier {
59    pub scheme: String,
60    pub value: String,
61}
62
63/// Time period for a context (instant or duration)
64#[derive(Debug, Clone, PartialEq)]
65pub enum Period {
66    /// A specific point in time
67    Instant { date: String },
68    /// A duration between two dates
69    Duration { start: String, end: String },
70    /// An open-ended period
71    Forever,
72}
73
74/// XBRL context combining entity, period, and optional dimensions
75#[derive(Debug, Clone, PartialEq)]
76pub struct Context {
77    /// The `id` attribute of the `<xbrli:context>` element.
78    pub id: ContextId,
79    /// The reporting entity (e.g., a company identified by a LEI).
80    pub entity: EntityIdentifier,
81    /// The time period this context applies to (e.g., an instant or duration).
82    pub period: Period,
83    /// Optional dimensions (e.g., segment, scenario) as a map from dimension
84    /// name to member name.
85    pub dimensions: HashMap<ExpandedName, ExpandedName>,
86    /// Track which dimensions were defined in segments.
87    pub segment_elements: Vec<ExpandedName>,
88    /// Track which dimensions were defined in scenarios.   
89    pub scenario_elements: Vec<ExpandedName>,
90    /// True if any segment element has an instance descendant (directly or via
91    /// substitution group).
92    pub segment_has_instance_descendant: bool,
93    /// True if any scenario element has an instance descendant (directly or via
94    /// substitution group).
95    pub scenario_has_instance_descendant: bool,
96}
97
98impl Context {
99    pub fn new(id: ContextId, entity: EntityIdentifier, period: Period) -> Self {
100        Self {
101            id,
102            entity,
103            period,
104            dimensions: HashMap::new(),
105            segment_elements: Vec::new(),
106            scenario_elements: Vec::new(),
107            segment_has_instance_descendant: false,
108            scenario_has_instance_descendant: false,
109        }
110    }
111
112    pub fn add_dimension(&mut self, dimension: ExpandedName, member: ExpandedName) {
113        self.dimensions.insert(dimension, member);
114    }
115}