1use std::collections::{BTreeMap, BTreeSet};
28use std::fmt::{self, Display, Formatter};
29use std::ops::Index;
30
31use amplify::confinement::{self, Confined, MediumOrdMap};
32use amplify::num::u24;
33use encoding::{LibName, Sizing, StrictDeserialize, StrictSerialize, TypeName};
34use strict_encoding::STRICT_TYPES_LIB;
35
36use crate::ast::UnnamedFields;
37use crate::{SemId, Ty};
38
39#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error)]
40#[display("type with id `{0}` is not a part of the type system.")]
41pub struct UnknownType(SemId);
42
43#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
44#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
45#[strict_type(lib = STRICT_TYPES_LIB)]
46#[display("{lib}.{name}")]
47#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
48pub struct TypeFqn {
49 pub lib: LibName,
50 pub name: TypeName,
51}
52
53impl TypeFqn {
54 pub fn with(lib: impl Into<LibName>, name: impl Into<TypeName>) -> TypeFqn {
55 TypeFqn {
56 lib: lib.into(),
57 name: name.into(),
58 }
59 }
60}
61
62impl From<&'static str> for TypeFqn {
63 fn from(value: &'static str) -> Self {
64 let Some((lib, name)) = value.split_once('.') else {
65 panic!("invalid fully qualified type name `{value}`");
66 };
67 TypeFqn {
68 lib: LibName::from(lib),
69 name: TypeName::from(name),
70 }
71 }
72}
73
74#[derive(Clone, Eq, PartialEq, Debug)]
76#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
77#[strict_type(lib = STRICT_TYPES_LIB)]
78#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
79pub struct SymTy {
80 pub orig: Option<TypeFqn>,
83 pub ty: Ty<SemId>,
85}
86
87impl SymTy {
88 pub fn named(lib_name: LibName, ty_name: TypeName, ty: Ty<SemId>) -> SymTy {
89 Self::with(Some(TypeFqn::with(lib_name, ty_name)), ty)
90 }
91
92 pub fn unnamed(ty: Ty<SemId>) -> SymTy { Self::with(None::<TypeFqn>, ty) }
93
94 pub fn with(orig: Option<impl Into<TypeFqn>>, ty: Ty<SemId>) -> SymTy {
95 SymTy {
96 orig: orig.map(|o| o.into()),
97 ty,
98 }
99 }
100}
101
102#[derive(Wrapper, Clone, Eq, PartialEq, Debug, Default, From)]
113#[wrapper(Deref)]
114#[derive(StrictType, StrictEncode, StrictDecode)]
115#[strict_type(lib = STRICT_TYPES_LIB)]
116#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
117pub struct TypeSystem(MediumOrdMap<SemId, Ty<SemId>>);
118
119impl StrictSerialize for TypeSystem {}
120impl StrictDeserialize for TypeSystem {}
121
122impl TypeSystem {
123 pub fn new() -> Self { Self::default() }
124
125 pub fn count_types(&self) -> u24 { self.0.len_u24() }
126
127 pub(super) fn insert_unchecked(
128 &mut self,
129 sem_id: SemId,
130 ty: Ty<SemId>,
131 ) -> Result<bool, confinement::Error> {
132 self.0.insert(sem_id, ty).map(|r| r.is_some())
133 }
134
135 pub fn get(&self, sem_id: SemId) -> Option<&Ty<SemId>> { self.0.get(&sem_id) }
136
137 pub fn extend(&mut self, other: Self) -> Result<(), confinement::Error> {
138 self.0.extend(other.0)
139 }
140
141 pub fn extract(&self, ids: impl IntoIterator<Item = SemId>) -> Result<Self, UnknownType> {
142 let mut ids = ids.into_iter().collect::<BTreeSet<_>>();
143 let mut found = BTreeSet::new();
144 let mut extract = BTreeMap::<SemId, Ty<SemId>>::new();
145
146 while let Some(id) = ids.pop_first() {
147 let ty = self.get(id).ok_or(UnknownType(id))?.clone();
148 found.insert(id);
149 ids.extend(ty.iter().filter(|(id, _)| !found.contains(*id)).map(|(id, _)| *id));
150 extract.insert(id, ty);
151 }
152
153 Ok(Self(Confined::from_checked(extract)))
154 }
155
156 pub(crate) fn rstring_sizing(
157 &self,
158 fields: &UnnamedFields<SemId>,
159 ) -> Result<Option<(SemId, Sizing)>, UnknownType> {
160 let rest = fields[1];
161 let rest = self.find(rest).ok_or(UnknownType(rest))?;
162 if let Ty::List(rest, sizing) = rest {
163 let mut sizing = *sizing;
164 sizing.min += 1;
165 sizing.max += 1;
166 return Ok(Some((*rest, sizing)));
167 }
168 Ok(None)
169 }
170
171 pub(crate) fn is_rstring(&self, fields: &UnnamedFields<SemId>) -> Result<bool, UnknownType> {
172 if fields.len() != 2 {
173 return Ok(false);
174 }
175 let first = fields[0];
176 let Some((rest, _)) = self.rstring_sizing(fields)? else {
177 return Ok(false);
178 };
179
180 Ok(self.find(first).ok_or(UnknownType(first))?.is_char_enum()
181 && self.find(rest).ok_or(UnknownType(rest))?.is_char_enum())
182 }
183}
184
185impl Index<SemId> for TypeSystem {
186 type Output = Ty<SemId>;
187
188 fn index(&self, index: SemId) -> &Self::Output {
189 self.get(index).unwrap_or_else(|| {
190 panic!("type with semantic id {index} is not a part of the type system")
191 })
192 }
193}
194
195impl Display for TypeSystem {
196 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
197 writeln!(f, "typesys -- {}", self.id())?;
198 writeln!(f)?;
199 for (id, ty) in &self.0 {
200 writeln!(f, "data {id:-}: {ty:-}")?;
201 }
202 Ok(())
203 }
204}
205
206#[cfg(feature = "armor")]
207impl armor::StrictArmor for TypeSystem {
208 type Id = crate::TypeSysId;
209 const PLATE_TITLE: &'static str = "STRICT TYPE SYSTEM";
210
211 fn armor_id(&self) -> Self::Id { self.id() }
212}