1use super::CandidType;
2use crate::idl_hash;
3use std::cell::RefCell;
4use std::cmp::Ordering;
5use std::collections::BTreeMap;
6use std::fmt;
7
8#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
12pub struct TypeId {
13 id: usize,
14 pub name: &'static str,
15}
16impl TypeId {
17 pub fn of<T: ?Sized>() -> Self {
18 let name = std::any::type_name::<T>();
19 #[allow(function_casts_as_integer)]
20 TypeId {
21 id: TypeId::of::<T> as usize,
22 name,
23 }
24 }
25}
26impl std::fmt::Display for TypeId {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 let name = NAME.with(|n| n.borrow_mut().get(self));
29 write!(f, "{name}")
30 }
31}
32pub fn type_of<T>(_: &T) -> TypeId {
33 TypeId::of::<T>()
34}
35
36#[derive(Default)]
37struct TypeName {
38 type_name: BTreeMap<TypeId, String>,
39 name_index: BTreeMap<String, usize>,
40}
41impl TypeName {
42 fn get(&mut self, id: &TypeId) -> String {
43 match self.type_name.get(id) {
44 Some(n) => n.to_string(),
45 None => {
46 let name = id.name.split('<').next().unwrap();
50 let name = name.rsplit("::").next().unwrap();
51 let name = name
52 .chars()
53 .map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })
54 .collect::<String>()
55 .trim_end_matches('_')
56 .to_string();
57 let res = match self.name_index.get_mut(&name) {
58 None => {
59 self.name_index.insert(name.clone(), 0);
60 name
61 }
62 Some(v) => {
63 *v += 1;
64 format!("{name}_{v}")
65 }
66 };
67 self.type_name.insert(id.clone(), res.clone());
68 res
69 }
70 }
71 }
72}
73
74#[derive(Debug, Clone, Default, PartialEq, Eq)]
75pub struct TypeDocs {
76 pub named: BTreeMap<String, TypeDoc>,
77}
78
79#[derive(Debug, Clone, Default, PartialEq, Eq)]
80pub struct TypeDoc {
81 pub docs: Vec<String>,
82 pub fields: BTreeMap<u32, FieldDoc>,
83}
84
85impl TypeDoc {
86 pub fn is_empty(&self) -> bool {
87 self.docs.is_empty() && self.fields.is_empty()
88 }
89}
90
91#[derive(Debug, Clone, Default, PartialEq, Eq)]
92pub struct FieldDoc {
93 pub docs: Vec<String>,
94 pub ty: Option<Box<TypeDoc>>,
95}
96
97impl FieldDoc {
98 pub fn is_empty(&self) -> bool {
99 self.docs.is_empty()
100 && match self.ty.as_deref() {
101 None => true,
102 Some(doc) => doc.is_empty(),
103 }
104 }
105}
106
107#[derive(Default)]
119pub struct TypeContainer {
120 pub env: crate::TypeEnv,
121 pub docs: TypeDocs,
122}
123impl TypeContainer {
124 pub fn new() -> Self {
125 TypeContainer {
126 env: crate::TypeEnv::new(),
127 docs: TypeDocs::default(),
128 }
129 }
130 pub fn add<T: CandidType>(&mut self) -> Type {
131 let t = T::ty();
132 self.go(&t)
133 }
134 fn go(&mut self, t: &Type) -> Type {
135 match t.as_ref() {
136 TypeInner::Opt(t) => TypeInner::Opt(self.go(t)),
137 TypeInner::Vec(t) => TypeInner::Vec(self.go(t)),
138 TypeInner::Record(fs) => {
139 let res: Type = TypeInner::Record(
140 fs.iter()
141 .map(|Field { id, ty }| Field {
142 id: id.clone(),
143 ty: self.go(ty),
144 })
145 .collect(),
146 )
147 .into();
148 if t.is_tuple() {
149 return res;
150 }
151 let id = ID.with(|n| n.borrow().get(t).cloned());
152 if let Some(id) = id {
153 let name = id.to_string();
154 self.env.0.insert(name.clone(), res);
155 self.remember_named_doc(&id, &name);
156 TypeInner::Var(name)
157 } else {
158 return res;
161 }
162 }
163 TypeInner::Variant(fs) => {
164 let res: Type = TypeInner::Variant(
165 fs.iter()
166 .map(|Field { id, ty }| Field {
167 id: id.clone(),
168 ty: self.go(ty),
169 })
170 .collect(),
171 )
172 .into();
173 let id = ID.with(|n| n.borrow().get(t).cloned());
174 if let Some(id) = id {
175 let name = id.to_string();
176 self.env.0.insert(name.clone(), res);
177 self.remember_named_doc(&id, &name);
178 TypeInner::Var(name)
179 } else {
180 return res;
181 }
182 }
183 TypeInner::Knot(id) => {
184 let name = id.to_string();
185 let ty = ENV.with(|e| e.borrow().get(id).unwrap().clone());
186 self.env.0.insert(id.to_string(), ty);
187 self.remember_named_doc(id, &name);
188 TypeInner::Var(name)
189 }
190 TypeInner::Func(func) => TypeInner::Func(Function {
191 modes: func.modes.clone(),
192 args: func.args.iter().map(|arg| self.go(arg)).collect(),
193 rets: func.rets.iter().map(|arg| self.go(arg)).collect(),
194 }),
195 TypeInner::Service(serv) => TypeInner::Service(
196 serv.iter()
197 .map(|(id, t)| (id.clone(), self.go(t)))
198 .collect(),
199 ),
200 TypeInner::Class(inits, ref ty) => {
201 TypeInner::Class(inits.iter().map(|t| self.go(t)).collect(), self.go(ty))
202 }
203 t => t.clone(),
204 }
205 .into()
206 }
207
208 fn remember_named_doc(&mut self, id: &TypeId, name: &str) {
209 if let Some(doc) = find_type_doc(id) {
210 if !doc.is_empty() {
211 self.docs.named.entry(name.to_string()).or_insert(doc);
212 }
213 }
214 }
215}
216
217#[derive(Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)]
218pub struct Type(pub std::rc::Rc<TypeInner>);
219
220#[derive(Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)]
221pub enum TypeInner {
222 Null,
223 Bool,
224 Nat,
225 Int,
226 Nat8,
227 Nat16,
228 Nat32,
229 Nat64,
230 Int8,
231 Int16,
232 Int32,
233 Int64,
234 Float32,
235 Float64,
236 Text,
237 Reserved,
238 Empty,
239 Knot(TypeId), Var(String), Unknown,
242 Opt(Type),
243 Vec(Type),
244 Record(Vec<Field>),
245 Variant(Vec<Field>),
246 Func(Function),
247 Service(Vec<(String, Type)>),
248 Class(Vec<Type>, Type),
249 Principal,
250 Future,
251}
252impl std::ops::Deref for Type {
253 type Target = TypeInner;
254 fn deref(&self) -> &TypeInner {
255 &self.0
256 }
257}
258impl AsRef<TypeInner> for Type {
259 fn as_ref(&self) -> &TypeInner {
260 self.0.as_ref()
261 }
262}
263impl From<TypeInner> for Type {
264 fn from(t: TypeInner) -> Self {
265 Type(t.into())
266 }
267}
268impl TypeInner {
269 pub fn is_tuple(&self) -> bool {
270 match self {
271 TypeInner::Record(ref fs) => {
272 for (i, field) in fs.iter().enumerate() {
273 if field.id.get_id() != (i as u32) {
274 return false;
275 }
276 }
277 true
278 }
279 _ => false,
280 }
281 }
282 pub fn is_blob(&self, env: &crate::TypeEnv) -> bool {
283 match self {
284 TypeInner::Vec(t) => {
285 let Ok(t) = env.trace_type(t) else {
286 return false;
287 };
288 matches!(*t, TypeInner::Nat8)
289 }
290 _ => false,
291 }
292 }
293}
294impl Type {
295 pub fn is_tuple(&self) -> bool {
296 self.as_ref().is_tuple()
297 }
298 pub fn is_blob(&self, env: &crate::TypeEnv) -> bool {
299 self.as_ref().is_blob(env)
300 }
301 pub fn subst(&self, tau: &std::collections::BTreeMap<String, String>) -> Self {
302 use TypeInner::*;
303 match self.as_ref() {
304 Var(id) => match tau.get(id) {
305 None => Var(id.to_string()),
306 Some(new_id) => Var(new_id.to_string()),
307 },
308 Opt(t) => Opt(t.subst(tau)),
309 Vec(t) => Vec(t.subst(tau)),
310 Record(fs) => Record(
311 fs.iter()
312 .map(|Field { id, ty }| Field {
313 id: id.clone(),
314 ty: ty.subst(tau),
315 })
316 .collect(),
317 ),
318 Variant(fs) => Variant(
319 fs.iter()
320 .map(|Field { id, ty }| Field {
321 id: id.clone(),
322 ty: ty.subst(tau),
323 })
324 .collect(),
325 ),
326 Func(func) => {
327 let func = func.clone();
328 Func(Function {
329 modes: func.modes,
330 args: func.args.into_iter().map(|t| t.subst(tau)).collect(),
331 rets: func.rets.into_iter().map(|t| t.subst(tau)).collect(),
332 })
333 }
334 Service(serv) => Service(
335 serv.iter()
336 .map(|(meth, ty)| (meth.clone(), ty.subst(tau)))
337 .collect(),
338 ),
339 Class(args, ty) => Class(args.iter().map(|t| t.subst(tau)).collect(), ty.subst(tau)),
340 _ => return self.clone(),
341 }
342 .into()
343 }
344}
345#[cfg(feature = "printer")]
346impl fmt::Display for Type {
347 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
348 write!(f, "{}", crate::pretty::candid::pp_ty(self).pretty(80))
349 }
350}
351#[cfg(feature = "printer")]
352impl fmt::Display for TypeInner {
353 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
354 write!(f, "{}", crate::pretty::candid::pp_ty_inner(self).pretty(80))
355 }
356}
357#[cfg(not(feature = "printer"))]
358impl fmt::Display for Type {
359 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
360 write!(f, "{:?}", self)
361 }
362}
363#[cfg(not(feature = "printer"))]
364impl fmt::Display for TypeInner {
365 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
366 write!(f, "{:?}", self)
367 }
368}
369#[allow(clippy::result_unit_err)]
370pub fn text_size(t: &Type, limit: i32) -> Result<i32, ()> {
371 use TypeInner::*;
372 if limit <= 1 {
373 return Err(());
374 }
375 let cost = match t.as_ref() {
376 Null | Bool | Text | Nat8 | Int8 => 4,
377 Nat | Int => 3,
378 Nat16 | Nat32 | Nat64 | Int16 | Int32 | Int64 | Empty => 5,
379 Float32 | Float64 => 7,
380 Reserved => 8,
381 Principal => 9,
382 Knot(_) => 10,
383 Var(id) => id.len() as i32,
384 Opt(t) => 4 + text_size(t, limit - 4)?,
385 Vec(t) => 4 + text_size(t, limit - 4)?,
386 Record(fs) | Variant(fs) => {
387 let mut cnt = 0;
388 let mut limit = limit;
389 for f in fs {
390 let id_size = match f.id.as_ref() {
391 Label::Named(n) => n.len() as i32,
392 Label::Id(_) => 4,
393 Label::Unnamed(_) => 0,
394 };
395 cnt += id_size + text_size(&f.ty, limit - id_size - 3)? + 3;
396 limit -= cnt;
397 }
398 9 + cnt
399 }
400 Func(func) => {
401 let mode = if func.modes.is_empty() { 0 } else { 6 };
402 let mut cnt = mode + 6;
403 let mut limit = limit - cnt;
404 for t in &func.args {
405 cnt += text_size(t, limit)?;
406 limit -= cnt;
407 }
408 for t in &func.rets {
409 cnt += text_size(t, limit)?;
410 limit -= cnt;
411 }
412 cnt
413 }
414 Service(ms) => {
415 let mut cnt = 0;
416 let mut limit = limit;
417 for (name, f) in ms {
418 let len = name.len() as i32;
419 cnt += len + text_size(f, limit - len - 3)? + 3;
420 limit -= cnt;
421 }
422 10 + cnt
423 }
424 Future => 6,
425 Unknown => 7,
426 Class(..) => unreachable!(),
427 };
428 if cost > limit {
429 Err(())
430 } else {
431 Ok(cost)
432 }
433}
434
435#[derive(Debug, Clone)]
436pub enum Label {
437 Id(u32),
438 Named(String),
439 Unnamed(u32),
440}
441
442impl Label {
443 pub fn get_id(&self) -> u32 {
444 match *self {
445 Label::Id(n) | Label::Unnamed(n) => n,
446 Label::Named(ref n) => idl_hash(n),
447 }
448 }
449}
450
451impl std::fmt::Display for Label {
452 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
453 match self {
454 Label::Id(n) | Label::Unnamed(n) => {
455 write!(f, "{}", crate::utils::pp_num_str(&n.to_string()))
456 }
457 Label::Named(id) => write!(f, "{id}"),
458 }
459 }
460}
461
462impl PartialEq for Label {
463 fn eq(&self, other: &Self) -> bool {
464 self.get_id() == other.get_id()
465 }
466}
467
468impl Eq for Label {}
469
470impl PartialOrd for Label {
471 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
472 Some(self.cmp(other))
473 }
474}
475
476impl Ord for Label {
477 fn cmp(&self, other: &Self) -> Ordering {
478 self.get_id().cmp(&other.get_id())
479 }
480}
481
482impl std::hash::Hash for Label {
483 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
484 state.write_u32(self.get_id());
485 }
486}
487
488pub type SharedLabel = std::rc::Rc<Label>;
489
490#[derive(Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)]
491pub struct Field {
492 pub id: SharedLabel,
493 pub ty: Type,
494}
495#[cfg(feature = "printer")]
496impl fmt::Display for Field {
497 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
498 write!(
499 f,
500 "{}",
501 crate::pretty::candid::pp_field(self, false).pretty(80)
502 )
503 }
504}
505#[cfg(not(feature = "printer"))]
506impl fmt::Display for Field {
507 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
508 write!(f, "{:?}", self)
509 }
510}
511
512#[macro_export]
513macro_rules! field {
518 { $id:tt : $ty:expr } => {{
519 $crate::types::internal::Field {
520 id: match stringify!($id).parse::<u32>() {
521 Ok(id) => $crate::types::Label::Id(id),
522 Err(_) => $crate::types::Label::Named(stringify!($id).to_string()),
523 }.into(),
524 ty: $ty
525 }
526 }}
527}
528#[macro_export]
529macro_rules! record {
531 { $($id:tt : $ty:expr);* $(;)? } => {{
532 let mut fs: Vec<$crate::types::internal::Field> = vec![ $($crate::field!{$id : $ty}),* ];
533 fs.sort_unstable_by_key(|f| f.id.get_id());
534 if let Err(e) = $crate::utils::check_unique(fs.iter().map(|f| &f.id)) {
535 panic!("{e}");
536 }
537 Into::<$crate::types::Type>::into($crate::types::TypeInner::Record(fs))
538 }}
539}
540#[macro_export]
541macro_rules! variant {
543 { $($id:tt : $ty:expr);* $(;)? } => {{
544 let mut fs: Vec<$crate::types::internal::Field> = vec![ $($crate::field!{$id : $ty}),* ];
545 fs.sort_unstable_by_key(|f| f.id.get_id());
546 if let Err(e) = $crate::utils::check_unique(fs.iter().map(|f| &f.id)) {
547 panic!("{e}");
548 }
549 Into::<$crate::types::Type>::into($crate::types::TypeInner::Variant(fs))
550 }}
551}
552
553#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
554pub enum FuncMode {
555 Oneway,
556 Query,
557 CompositeQuery,
558}
559#[derive(Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)]
560pub struct Function {
561 pub modes: Vec<FuncMode>,
562 pub args: Vec<Type>,
563 pub rets: Vec<Type>,
564}
565
566#[cfg(feature = "printer")]
567impl fmt::Display for Function {
568 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
569 write!(f, "{}", crate::pretty::candid::pp_function(self).pretty(80))
570 }
571}
572#[cfg(not(feature = "printer"))]
573impl fmt::Display for Function {
574 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
575 write!(f, "{:?}", self)
576 }
577}
578impl Function {
579 pub fn is_query(&self) -> bool {
581 self.modes
582 .iter()
583 .any(|m| matches!(m, FuncMode::Query | FuncMode::CompositeQuery))
584 }
585}
586#[macro_export]
587macro_rules! func {
591 ( ( $($arg:ty),* $(,)? ) -> ( $($ret:ty),* $(,)? ) ) => {
592 Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*], rets: vec![$(<$ret>::ty()),*], modes: vec![] }))
593 };
594 ( ( $($arg:ty),* $(,)? ) -> ( $($ret:ty),* $(,)? ) query ) => {
595 Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*], rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::Query] }))
596 };
597 ( ( $($arg:ty),* $(,)? ) -> ( $($ret:ty),* $(,)? ) composite_query ) => {
598 Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*], rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::CompositeQuery] }))
599 };
600 ( ( $($arg:ty),* $(,)? ) -> ( $($ret:ty),* $(,)? ) oneway ) => {
601 Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*], rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::Oneway] }))
602 };
603}
604#[macro_export]
605macro_rules! service {
609 { $($meth:tt : $ty:expr);* $(;)? } => {{
610 let mut ms: Vec<(String, $crate::types::Type)> = vec![ $(($meth.to_string(), $ty)),* ];
611 ms.sort_unstable_by(|a, b| a.0.as_str().partial_cmp(b.0.as_str()).unwrap());
612 if let Err(e) = $crate::utils::check_unique(ms.iter().map(|m| &m.0)) {
613 panic!("{e}");
614 }
615 Into::<$crate::types::Type>::into($crate::types::TypeInner::Service(ms))
616 }}
617}
618
619#[derive(Debug, PartialEq)]
620#[repr(i64)]
621pub enum Opcode {
622 Null = -1,
623 Bool = -2,
624 Nat = -3,
625 Int = -4,
626 Nat8 = -5,
627 Nat16 = -6,
628 Nat32 = -7,
629 Nat64 = -8,
630 Int8 = -9,
631 Int16 = -10,
632 Int32 = -11,
633 Int64 = -12,
634 Float32 = -13,
635 Float64 = -14,
636 Text = -15,
637 Reserved = -16,
638 Empty = -17,
639 Opt = -18,
640 Vec = -19,
641 Record = -20,
642 Variant = -21,
643 Func = -22,
644 Service = -23,
645 Principal = -24,
646}
647
648pub fn is_primitive(t: &Type) -> bool {
649 use self::TypeInner::*;
650 match t.as_ref() {
651 Null | Bool | Nat | Int | Text => true,
652 Nat8 | Nat16 | Nat32 | Nat64 => true,
653 Int8 | Int16 | Int32 | Int64 => true,
654 Float32 | Float64 => true,
655 Reserved | Empty => true,
656 Unknown => panic!("Unknown type"),
657 Future => panic!("Future type"),
658 Var(_) => panic!("Variable"), Knot(_) => true,
660 Opt(_) | Vec(_) | Record(_) | Variant(_) => false,
661 Func(_) | Service(_) | Class(_, _) => false,
662 Principal => true,
663 }
664}
665
666pub fn unroll(t: &Type) -> Type {
667 use self::TypeInner::*;
668 match t.as_ref() {
669 Knot(ref id) => return find_type(id).unwrap(),
670 Opt(ref t) => Opt(unroll(t)),
671 Vec(ref t) => Vec(unroll(t)),
672 Record(fs) => Record(
673 fs.iter()
674 .map(|Field { id, ty }| Field {
675 id: id.clone(),
676 ty: unroll(ty),
677 })
678 .collect(),
679 ),
680 Variant(fs) => Variant(
681 fs.iter()
682 .map(|Field { id, ty }| Field {
683 id: id.clone(),
684 ty: unroll(ty),
685 })
686 .collect(),
687 ),
688 t => t.clone(),
689 }
690 .into()
691}
692
693thread_local! {
694 static ENV: RefCell<BTreeMap<TypeId, Type>> = const { RefCell::new(BTreeMap::new()) };
695 static DOC_ENV: RefCell<BTreeMap<TypeId, TypeDoc>> = const { RefCell::new(BTreeMap::new()) };
696 static ID: RefCell<BTreeMap<Type, TypeId>> = const { RefCell::new(BTreeMap::new()) };
698 static NAME: RefCell<TypeName> = RefCell::new(TypeName::default());
699}
700
701pub fn find_type(id: &TypeId) -> Option<Type> {
702 ENV.with(|e| e.borrow().get(id).cloned())
703}
704
705pub fn find_type_doc(id: &TypeId) -> Option<TypeDoc> {
706 DOC_ENV.with(|e| e.borrow().get(id).cloned())
707}
708
709#[allow(dead_code)]
711pub(crate) fn show_env() {
712 ENV.with(|e| println!("{:?}", e.borrow()));
713}
714
715pub(crate) fn env_add(id: TypeId, t: Type) {
716 ENV.with(|e| e.borrow_mut().insert(id, t));
717}
718pub fn env_clear() {
719 ENV.with(|e| e.borrow_mut().clear());
720 DOC_ENV.with(|e| e.borrow_mut().clear());
721}
722
723pub(crate) fn env_id(id: TypeId, t: Type) {
724 let new_len = id.name.len();
726 ID.with(|n| {
727 let mut n = n.borrow_mut();
728 match n.get_mut(&t) {
729 None => {
730 n.insert(t, id);
731 }
732 Some(v) => {
733 if new_len < v.name.len() {
734 *v = id;
735 }
736 }
737 }
738 });
739}
740
741pub(crate) fn env_doc(id: TypeId, doc: TypeDoc) {
742 DOC_ENV.with(|e| e.borrow_mut().insert(id, doc));
743}
744
745pub fn get_type<T>(_v: &T) -> Type
746where
747 T: CandidType,
748{
749 T::ty()
750}