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