Skip to main content

candid/types/
internal.rs

1use super::CandidType;
2use crate::idl_hash;
3use std::cell::RefCell;
4use std::cmp::Ordering;
5use std::collections::BTreeMap;
6use std::fmt;
7
8// This is a re-implementation of std::any::TypeId to get rid of 'static constraint.
9// The current TypeId doesn't consider lifetime while computing the hash, which is
10// totally fine for Candid type, as we don't care about lifetime at all.
11#[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                // The format of id.name is unspecified, and doesn't guarantee to be unique.
46                // Splitting by "::" is not ideal, as we can get types like std::Box<lib::List>, HashMap<lib::K, V>
47                // This is not a problem for correctness, but I may get misleading names.
48                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/// Used for `candid_derive::export_service` to generate `TypeEnv` from `Type`.
107///
108/// It performs a global rewriting of `Type` to resolve:
109/// * Duplicate type names in different modules/namespaces.
110/// * Give different names to instantiated polymorphic types.
111/// * Find the type name of a recursive node `Knot(TypeId)` and convert to `Var` node.
112///
113/// There are some drawbacks of this approach:
114/// * The type name is based on `type_name::<T>()`, whose format is unspecified and long. We use some regex to shorten the name.
115/// * Several Rust types can map to the same Candid type, and we only get to remember one name (currently we choose the shortest name). As a result, some of the type names in Rust is lost.
116/// * Unless we do equivalence checking, recursive types can be unrolled and assigned to multiple names.
117#[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                    // if the type is part of an enum, the id won't be recorded.
158                    // we want to inline the type in this case.
159                    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), // For recursive types from Rust
239    Var(String),  // For variables from Candid file
240    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]
512/// Construct a field type, which can be used in `TypeInner::Record` and `TypeInner::Variant`.
513///
514/// `field!{ a: TypeInner::Nat.into() }` expands to `Field { id: Label::Named("a"), ty: ... }`
515/// `field!{ 0: Nat::ty() }` expands to `Field { id: Label::Id(0), ty: ... }`
516macro_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]
528/// Construct a record type, e.g., `record!{ label: Nat::ty(); 42: String::ty() }`.
529macro_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]
540/// Construct a variant type, e.g., `variant!{ tag: <()>::ty() }`.
541macro_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    /// Check a function is a `query` or `composite_query` method
579    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]
586/// Construct a function type.
587///
588/// `func!((u8, &str) -> (Nat) query)` expands to `Type(Rc::new(TypeInner::Func(...)))`
589macro_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]
604/// Construct a service type.
605///
606/// `service!{ "f": func!((HttpRequest) -> ()) }` expands to `Type(Rc::new(TypeInner::Service(...)))`
607macro_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"), // Var may or may not be a primitive, so don't ask me
658        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    // only used for TypeContainer
696    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// only for debugging
709#[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    // prefer shorter type names
724    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}