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        #[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                // The format of id.name is unspecified, and doesn't guarantee to be unique.
47                // Splitting by "::" is not ideal, as we can get types like std::Box<lib::List>, HashMap<lib::K, V>
48                // This is not a problem for correctness, but I may get misleading names.
49                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/// Used for `candid_derive::export_service` to generate `TypeEnv` from `Type`.
108///
109/// It performs a global rewriting of `Type` to resolve:
110/// * Duplicate type names in different modules/namespaces.
111/// * Give different names to instantiated polymorphic types.
112/// * Find the type name of a recursive node `Knot(TypeId)` and convert to `Var` node.
113///
114/// There are some drawbacks of this approach:
115/// * The type name is based on `type_name::<T>()`, whose format is unspecified and long. We use some regex to shorten the name.
116/// * 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.
117/// * Unless we do equivalence checking, recursive types can be unrolled and assigned to multiple names.
118#[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                    // if the type is part of an enum, the id won't be recorded.
159                    // we want to inline the type in this case.
160                    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), // For recursive types from Rust
240    Var(String),  // For variables from Candid file
241    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]
513/// Construct a field type, which can be used in `TypeInner::Record` and `TypeInner::Variant`.
514///
515/// `field!{ a: TypeInner::Nat.into() }` expands to `Field { id: Label::Named("a"), ty: ... }`
516/// `field!{ 0: Nat::ty() }` expands to `Field { id: Label::Id(0), ty: ... }`
517macro_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]
529/// Construct a record type, e.g., `record!{ label: Nat::ty(); 42: String::ty() }`.
530macro_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]
541/// Construct a variant type, e.g., `variant!{ tag: <()>::ty() }`.
542macro_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    /// Check a function is a `query` or `composite_query` method
580    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]
587/// Construct a function type.
588///
589/// `func!((u8, &str) -> (Nat) query)` expands to `Type(Rc::new(TypeInner::Func(...)))`
590macro_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]
605/// Construct a service type.
606///
607/// `service!{ "f": func!((HttpRequest) -> ()) }` expands to `Type(Rc::new(TypeInner::Service(...)))`
608macro_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"), // Var may or may not be a primitive, so don't ask me
659        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    // only used for TypeContainer
697    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// only for debugging
710#[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    // prefer shorter type names
725    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}