scale_info_legacy/
insert_name.rs

1// Copyright (C) 2024 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the scale-info-legacy crate.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//         http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! This module provides the name used to insert types in a registry.
17
18use crate::lookup_name::{LookupName, LookupNameDef};
19use alloc::borrow::Cow;
20use alloc::borrow::ToOwned;
21use alloc::format;
22use alloc::string::{String, ToString};
23use core::fmt::Write;
24use smallvec::SmallVec;
25
26/// An error constructing an [`InsertName`].
27#[allow(missing_docs)]
28#[derive(Debug, thiserror::Error)]
29pub enum ParseError {
30    #[error("Failed to parse the string. Expected something like 'Foo' or 'Bar<A, B>'.")]
31    Invalid,
32    #[error("Expected the generic params to be names like 'A' or 'B', not arrays or tuples.")]
33    ExpectingNamedParam,
34    #[error("Expected the generic params to be capitalized.")]
35    ExpectingUppercaseParams,
36}
37
38/// A name used as a key when inserting a type into a [`crate::TypeRegistry`].
39///
40/// # Example
41///
42/// ```rust
43/// use scale_info_legacy::InsertName;
44///
45/// // Names can be plain identifiers:
46/// InsertName::parse("Foo").unwrap();
47/// // Or they can have generic parameters which
48/// // will be resolved during lookup:
49/// InsertName::parse("Bar<A,B>").unwrap();
50/// ```
51#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
52pub struct InsertName {
53    pub(crate) name: String,
54    pub(crate) params: SmallVec<[String; 4]>,
55    pub(crate) pallet: Option<String>,
56}
57
58impl InsertName {
59    /// Parse a string into an [`InsertName`].
60    pub fn parse(s: &str) -> Result<Self, ParseError> {
61        let ty_name = LookupName::parse(s).map_err(|_| ParseError::Invalid)?;
62        ty_name.try_into()
63    }
64
65    /// Scope the inserted type to being within the given pallet. Only lookups that are
66    /// also performed within this pallet will make use of this type.
67    pub fn in_pallet(mut self, pallet_name: impl Into<String>) -> InsertName {
68        self.pallet = Some(pallet_name.into());
69        self
70    }
71}
72
73impl core::str::FromStr for InsertName {
74    type Err = ParseError;
75    fn from_str(s: &str) -> Result<Self, Self::Err> {
76        Self::parse(s)
77    }
78}
79
80impl core::convert::TryFrom<LookupName> for InsertName {
81    type Error = ParseError;
82    fn try_from(mut ty_name: LookupName) -> Result<Self, Self::Error> {
83        let pallet = ty_name.take_pallet();
84
85        // We only accept named types like Foo<A, B> or path::to::Bar.
86        let LookupNameDef::Named(named_ty) = ty_name.def() else {
87            return Err(ParseError::Invalid);
88        };
89
90        let name = named_ty.name().to_owned();
91        let params = named_ty
92            .param_defs()
93            .map(|param| {
94                // Params must be simple names and not array/tuples.
95                let LookupNameDef::Named(name) = param else {
96                    return Err(ParseError::ExpectingNamedParam);
97                };
98                // Param names must be capitalized because they represent generics.
99                if name.name().starts_with(|c: char| c.is_lowercase()) {
100                    return Err(ParseError::ExpectingUppercaseParams);
101                }
102                Ok(name.name().to_owned())
103            })
104            .collect::<Result<_, _>>()?;
105
106        Ok(InsertName { name, params, pallet })
107    }
108}
109
110impl core::convert::TryFrom<&str> for InsertName {
111    type Error = ParseError;
112    fn try_from(s: &str) -> Result<Self, Self::Error> {
113        Self::parse(s)
114    }
115}
116
117impl core::convert::TryFrom<String> for InsertName {
118    type Error = ParseError;
119    fn try_from(s: String) -> Result<Self, Self::Error> {
120        Self::parse(&s)
121    }
122}
123
124impl core::fmt::Debug for InsertName {
125    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
126        write!(f, "{}", self)
127    }
128}
129
130impl core::fmt::Display for InsertName {
131    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
132        f.write_str(&self.name)?;
133        if !self.params.is_empty() {
134            f.write_char('<')?;
135            for (idx, param) in self.params.iter().enumerate() {
136                if idx != 0 {
137                    f.write_str(", ")?;
138                }
139                f.write_str(param)?;
140            }
141            f.write_char('>')?;
142        }
143        Ok(())
144    }
145}
146
147impl serde::Serialize for InsertName {
148    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
149    where
150        S: serde::Serializer,
151    {
152        serializer.serialize_str(&self.to_string())
153    }
154}
155
156impl<'de> serde::Deserialize<'de> for InsertName {
157    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
158    where
159        D: serde::Deserializer<'de>,
160    {
161        use serde::de::Error;
162        let s = <Cow<'de, str>>::deserialize(deserializer)?;
163        InsertName::parse(&s)
164            .map_err(|e| D::Error::custom(format!("Could not deserialize into InsertName: {e}")))
165    }
166}