/// Diplomat attribute that can be specified on items, methods, and enum variants. These /// can be used to control the codegen in a particular backend. /// /// Most of these are specified via `#[diplomat::attr(some cfg here, attrname)]`, where `some cfg here` /// can be used to pick which backends something applies to. #[non_exhaustive] #[derive(Clone, Default, Debug)] pubstruct Attrs { /// "disable" this item: do not generate code for it in the backend /// /// This attribute is always inherited except to variants pub disable: bool, /// Mark this item deprecated in FFI. pub deprecated: Option<String>, /// An optional namespace. None is equivalent to the root namespace. /// /// This attribute is inherited to types (and is not allowed elsewhere) pub namespace: Option<String>, /// Rename this item/method/variant /// /// This attribute is inherited except through methods and variants (and is not allowed on variants) pub rename: RenameAttr, /// Rename this item in the C ABI. This *must* be respected by backends. /// /// This attribute is inherited except through variants pub abi_rename: RenameAttr, /// This method is "special": it should generate something other than a regular method on the other side. /// This can be something like a constructor, an accessor, a stringifier etc. /// /// This attribute does not participate in inheritance and must always /// be specified on individual methods pub special_method: Option<SpecialMethod>, /// This user-defined type can be used as the error type in a Result. pub custom_errors: bool,
/// This user-defined type has a "default" state that can be computed purely foreign-language-side /// /// Can be applied to enum variants to signal the default pub default: bool,
/// From #[diplomat::demo()]. Created from [`crate::ast::attrs::Attrs::demo_attrs`]. /// List of attributes specific to automatic demo generation. /// Currently just for demo_gen in diplomat-tool (which generates sample webpages), but could be used for broader purposes (i.e., demo Android apps) pub demo_attrs: DemoInfo, /// From #[diplomat::attr()]. If true, generates a mocking interface for this type. pub generate_mocking_interface: bool, /// From #[diplomat::attr()]. If true, Diplomat will check that this struct has the same memory layout in backends which support it. Allows this struct to be used in slices ([`super::Slice::Struct`]) and to be borrowed in function parameters. pub abi_compatible: bool,
}
// #region: Demo specific attributes.
/// For `#[diplomat::demo(input(...))]`, stored in [DemoInfo::input_cfg]. #[non_exhaustive] #[derive(Clone, Default, Debug)] pubstruct DemoInputCFG { /// `#[diplomat::demo(input(label = "..."))]` /// Label that this input parameter should have. Let demo_gen pick a valid name if this is empty. /// /// For instance <label for="v">Number Here</label><input name="v"/> pub label: String,
/// `#[diplomat::demo(input(default_value = "..."))]` /// Sets the default value for a parameter. /// /// Should ALWAYS be a string. The HTML renderer is expected to do validation for us. pub default_value: String,
}
#[non_exhaustive] #[derive(Clone, Default, Debug)] pubstruct DemoInfo { /// `#[diplomat::demo(generate)]`. If automatic generation is disabled by default (see [`diplomat_tool::demo_gen::DemoConfig`]), then the below render terminus will be allowed to generate. pub generate: bool,
/// `#[diplomat::demo(default_constructor)]` /// We search for any methods specially tagged with `Constructor`, but if there's are no default Constructors and there's NamedConstructor that you want to be default instead, use this. /// TODO: Should probably ignore other `Constructors` if a default has been set. pub default_constructor: bool,
/// `#[diplomat::demo(external)]` represents an item that we will not evaluate, and should be passed to the rendering engine to provide. pub external: bool,
/// `#[diplomat::demo(custom_func = "/file/name/here.mjs")]` can be used above any `struct` definition in the bridge. The linked `.mjs` should contain a JS definition of functions that should be bundled with demo_gen's output. /// /// We call these functions "custom functions", as they are JS functions that are not automagically generated by demo_gen, but rather included as part of its JS output in the `RenderInfo` object. /// /// For more information on custom functions (and their use), see the relevant chapter in [the book](https://rust-diplomat.github.io/diplomat/demo_gen/custom_functions.html). /// /// Files are located relative to lib.rs. /// pub custom_func: Option<String>,
/// `#[diplomat::demo(input(...))]` represents configuration options for anywhere we might expect user input. pub input_cfg: DemoInputCFG,
}
// #endregion
/// Attributes that mark methods as "special" #[non_exhaustive] #[derive(Clone, Debug)] pubenum SpecialMethod { /// A constructor. /// /// Must return Self (or Result<Self> for backends with `fallible_constructors` enabled )
Constructor, /// A named constructor, with optional name. If the name isn't specified, it will be derived /// from the method name /// /// Must return Self (or Result<Self> for backends with `fallible_constructors` enabled )
NamedConstructor(Option<String>),
/// A getter, with optional name. If the name isn't specified, it will be derived /// from the method name /// /// Must have no parameters and must return something.
Getter(Option<String>), /// A setter, with optional name. If the name isn't specified, it will be derived /// from the method name /// /// Must have no return type (aside from potentially a `Result<(), _>`) and must have one parameter
Setter(Option<String>), /// A stringifier. Must have no parameters and return a string (DiplomatWrite)
Stringifier, /// A comparison operator. Currently not universally supported
Comparison, /// An iterator (a type that is mutated to produce new values)
Iterator, /// An iterable (a type that can produce an iterator)
Iterable, /// Indexes into the type using an integer
Indexer,
/// Arithmetic operators. May not return references
Add,
Sub,
Mul,
Div,
/// In-place arithmetic operators. Must not return a value
AddAssign,
SubAssign,
MulAssign,
DivAssign,
}
impl SpecialMethod { pubfn from_path_and_meta(path: &str, meta: &Meta) -> Result<Option<Self>, LoweringError> { let parse_meta = |meta| match StandardAttribute::from_meta(meta) {
Ok(StandardAttribute::String(s)) => Ok(Some(s)),
Ok(StandardAttribute::Empty) => Ok(None),
Ok(_) | Err(_) => Err(LoweringError::Other(format!( "`{path}` must have a single string parameter or no parameter"
))),
};
/// For special methods that affect type semantics, whether this type has this method. /// /// This will likely only contain a subset of special methods, but feel free to add more as needed. #[derive(Debug, Default)] #[non_exhaustive] pubstruct SpecialMethodPresence { pub comparator: bool, /// If it is an iterator, the type it iterates over pub iterator: Option<SuccessType>, /// If it is an iterable, the iterator type it returns (*not* the type it iterates over, /// perform lookup on that type to access) pub iterable: Option<OpaqueId>,
}
/// Where the attribute was found. Some attributes are only allowed in some contexts /// (e.g. namespaces cannot be specified on methods) #[non_exhaustive] // might add module attrs in the future #[derive(Debug)] pubenum AttributeContext<'a, 'b> { Type(TypeDef<'a>), Trait(&'a TraitDef),
EnumVariant(&'a EnumVariant),
Method(&'a Method, Option<TypeId>, &'b mut SpecialMethodPresence),
Function(&'a Method),
Module,
Param,
SelfParam,
Field,
}
fn maybe_error_unsupported(
auto_found: bool,
attribute: &str,
backend: &str,
errors: &mut ErrorStore,
) { if !auto_found {
errors.push(LoweringError::Other(format!( "`{attribute}` not supported in backend {backend}"
)));
}
} impl Attrs { pubfn from_ast(
ast: &ast::Attrs,
validator: &(impl AttributeValidator + ?Sized),
parent_attrs: &Attrs,
errors: &mut ErrorStore,
) -> Self { letmut this = parent_attrs.clone(); // Backends must support this since it applies to the macro/C code. // No special inheritance, was already appropriately inherited in AST
this.abi_rename = ast.abi_rename.clone();
this.deprecated = ast.deprecated.clone();
let support = validator.attrs_supported(); let backend = validator.primary_name(); for attr in &ast.attrs { letmut auto_found = false; match validator.satisfies_cfg(&attr.cfg, Some(&mut auto_found)) {
Ok(satisfies) if !satisfies => continue,
Err(e) => {
errors.push(e); continue;
}
Ok(_) => {}
};
let path = attr.meta.path(); iflet Some(path) = path.get_ident() { let path = path.to_string();
let warn_auto = |errors: &mut ErrorStore| { if auto_found {
errors.push(LoweringError::Other(format!( "Diplomat attribute {path:?} gated on 'auto' but is not one that works with 'auto'"
)));
}
};
// Check against the set of attributes that can have platform support if support.check_string(&path) == Some(false) {
maybe_error_unsupported(auto_found, &path, backend, errors); continue;
}
match SpecialMethod::from_path_and_meta(&path, &attr.meta) {
Ok(Some(kind)) => { iflet Some(ref existing) = this.special_method {
errors.push(LoweringError::Other(format!( "Multiple special method markers found on the same method, found {path} and {existing:?}"
)));
} else {
this.special_method = Some(kind);
}
}
Err(error) => errors.push(error),
Ok(None) => match path.as_str() { // No match found in the special methods, check the other keywords "disable" => { iflet Meta::Path(_) = attr.meta { if this.disable {
errors.push(LoweringError::Other( "Duplicate `disable` attribute".into(),
));
} else {
this.disable = true;
}
} else {
errors.push(LoweringError::Other( "`disable` must be a simple path".into(),
))
}
warn_auto(errors);
} "default" => { iflet Meta::Path(_) = attr.meta { if this.default {
errors.push(LoweringError::Other( "Duplicate `default` attribute".into(),
));
} else {
this.default = true;
}
} else {
errors.push(LoweringError::Other( "`default` must be a simple path".into(),
))
}
} "rename" => { match RenameAttr::from_meta(&attr.meta) {
Ok(rename) => { // We use the override extend mode: a single ast::Attrs // will have had these attributes inherited into the list by appending // to the end; so a later attribute in the list is more pertinent.
this.rename.extend(&rename);
}
Err(e) => errors.push(LoweringError::Other(format!( "`rename` attr failed to parse: {e:?}"
))),
}
warn_auto(errors);
} "namespace" => match StandardAttribute::from_meta(&attr.meta) {
Ok(StandardAttribute::String(s)) if s.is_empty() => {
this.namespace = None
}
Ok(StandardAttribute::String(s)) => this.namespace = Some(s),
Ok(_) | Err(_) => {
errors.push(LoweringError::Other( "`namespace` must have a single string parameter".to_string(),
));
}
}, "error" => {
this.custom_errors = true;
} "generate_mocking_interface" => { if !support.generate_mocking_interface {
maybe_error_unsupported(
auto_found, "generate_mocking_interface",
backend,
errors,
); continue;
}
this.generate_mocking_interface = true;
} "abi_compatible" => { if !support.abi_compatibles {
maybe_error_unsupported(
auto_found, "abi_compatible",
backend,
errors,
); continue;
}
this.abi_compatible = true;
}
_ => {
errors.push(LoweringError::Other(format!( "Unknown diplomat attribute {path}: expected one of: `disable, rename, namespace, constructor, stringifier, comparison, named_constructor, getter, setter, indexer, error`"
)));
}
},
}
}
}
for attr in &ast.demo_attrs { let path = attr.meta.path(); iflet Some(path_ident) = path.get_ident() { if path_ident == "external" {
this.demo_attrs.external = true;
} elseif path_ident == "default_constructor" {
this.demo_attrs.default_constructor = true;
} elseif path_ident == "generate" {
this.demo_attrs.generate = true;
} elseif path_ident == "input" { let meta_list = attr
.meta
.require_list()
.expect("Could not get MetaList, expected #[diplomat::demo(input(...))]");
meta_list
.parse_nested_meta(|meta| { if meta.path.is_ident("label") { let value = meta.value()?; let s: syn::LitStr = value.parse()?;
this.demo_attrs.input_cfg.label = s.value();
Ok(())
} elseif meta.path.is_ident("default_value") { let value = meta.value()?;
let str_val: String;
let ahead = value.lookahead1(); if ahead.peek(syn::LitFloat) { let s: syn::LitFloat = value.parse()?;
str_val = s.base10_parse::<f64>()?.to_string();
} elseif ahead.peek(syn::LitInt) { let s: syn::LitInt = value.parse()?;
str_val = s.base10_parse::<i64>()?.to_string();
} else { let s: syn::LitStr = value.parse()?;
str_val = s.value();
}
this.demo_attrs.input_cfg.default_value = str_val;
Ok(())
} else {
Err(meta.error(format!( "Unsupported ident {:?}",
meta.path.get_ident()
)))
}
})
.expect("Could not read input(...)");
} elseif path_ident == "custom_func" { let v = &attr.meta.require_name_value().unwrap().value;
iflet syn::Expr::Lit(s) = v { iflet syn::Lit::Str(string) = &s.lit {
this.demo_attrs.custom_func = Some(string.value());
} else {
errors.push(LoweringError::Other(format!( "#[diplomat::demo(custom_func={s:?}) must be a literal string."
)));
}
} else {
errors.push(LoweringError::Other(format!( "#[diplomat::demo(custom_func={v:?}) must be a literal string."
)));
}
} else {
errors.push(LoweringError::Other(format!( "Unknown demo_attr: {path_ident:?}"
)));
}
} else {
errors.push(LoweringError::Other(format!("Unknown demo_attr: {path:?}")));
}
}
this
}
/// Validate that this attribute is allowed in this context pub(crate) fn validate(
&self,
validator: &(impl AttributeValidator + ?Sized), mut context: AttributeContext,
errors: &mut ErrorStore,
) { // use an exhaustive destructure so new attributes are handled let Attrs {
disable,
deprecated: _deprecated,
namespace,
rename,
abi_rename,
special_method,
custom_errors,
default,
demo_attrs: _,
generate_mocking_interface,
abi_compatible,
} = &self;
if *disable && matches!(context, AttributeContext::EnumVariant(..)) {
errors.push(LoweringError::Other( "`disable` cannot be used on enum variants".into(),
))
}
iflet Some(ref special) = special_method { iflet AttributeContext::Method(method, self_id, refmut special_method_presence) =
context
{ let check_param_count = |name: &str, count: usize, errors: &mut ErrorStore| { if method.params.len() != count {
errors.push(LoweringError::Other(format!( "{name} must have exactly {count} parameter{}", if count == 1 { "" } else { "s" }
)))
}
}; let check_self_param = |name: &str, need_self: bool, errors: &mut ErrorStore| { if method.param_self.is_some() != need_self {
errors.push(LoweringError::Other(format!( "{name} must{} accept a self parameter", if need_self { "" } else { " not" }
)));
}
}; match special {
SpecialMethod::Constructor | SpecialMethod::NamedConstructor(..) => {
check_self_param("Constructors", false, errors); let output = method.output.success_type(); match method.output {
ReturnType::Infallible(_) => (),
ReturnType::Fallible(..) => { // Only report an error if constructors *are* supported but failable constructors *arent* if validator.attrs_supported().constructors
&& !validator.attrs_supported().fallible_constructors
{
errors.push(LoweringError::Other( "This backend doesn't support fallible constructors"
.to_string(),
))
}
}
ReturnType::Nullable(..) => {
errors.push(LoweringError::Other("Diplomat doesn't support turning nullable methods into constructors".to_string()));
}
}
iflet SuccessType::OutType(t) = &output { if t.id() != self_id || self_id.is_none() {
errors.push(LoweringError::Other( "Constructors must return Self!".to_string(),
));
}
} else {
errors.push(LoweringError::Other( "Constructors must return Self!".to_string(),
));
}
}
SpecialMethod::Getter(_) => { if !method.params.is_empty() {
errors
.push(LoweringError::Other("Getter cannot have parameters".into()));
} if method.param_self.is_none()
&& !validator.attrs_supported().static_accessors
{
errors.push(LoweringError::Other(format!("No self parameter on Getter {} but static_acessors are not supported",method.name.as_str())));
}
// Currently does not forbid nullable getters, could if desired
}
SpecialMethod::Setter(_) => { if !matches!(method.output.success_type(), SuccessType::Unit) {
errors.push(LoweringError::Other("Setters must return unit".into()));
} if method.param_self.is_none()
&& !validator.attrs_supported().static_accessors
{
errors.push(LoweringError::Other(format!("No self parameter on Setter {} but static_acessors are not supported",method.name.as_str())));
}
check_param_count("Setter", 1, errors); // Currently does not forbid fallible setters, could if desired
}
SpecialMethod::Stringifier => { if !method.params.is_empty() {
errors
.push(LoweringError::Other("Getter cannot have parameters".into()));
} if !matches!(method.output.success_type(), SuccessType::Write) {
errors.push(LoweringError::Other( "Stringifier must return string".into(),
));
}
}
SpecialMethod::Comparison => {
check_param_count("Comparator", 1, errors); if special_method_presence.comparator {
errors.push(LoweringError::Other( "Cannot define two comparators on the same type".into(),
));
}
special_method_presence.comparator = true; // In the long run we can actually support heterogeneous comparators. Not a priority right now. const COMPARATOR_ERROR: &str = "Comparator's parameter must be identical to self";
check_self_param("Comparators", true, errors);
if p.owner.mutability != Mutability::Immutable
|| p2.owner.mutability != Mutability::Immutable
{
errors.push(LoweringError::Other( "comparators must accept immutable parameters"
.into(),
));
}
if p2.optional.0 {
errors.push(LoweringError::Other( "comparators must accept non-optional parameters"
.into(),
));
}
}
(SelfType::Struct(p), Type::Struct(p2)) => { if p.tcx_id != p2.tcx_id {
errors.push(LoweringError::Other(
COMPARATOR_ERROR.into(),
));
}
if p.owner
.as_borrowed()
.map(|o| !o.mutability.is_immutable())
.unwrap_or(false)
|| p2
.owner
.as_borrowed()
.map(|o| !o.mutability.is_immutable())
.unwrap_or(false)
{
errors.push(LoweringError::Other( "comparators must accept immutable parameters"
.into(),
));
}
}
(SelfType::Enum(p), Type::Enum(p2)) => { if p.tcx_id != p2.tcx_id {
errors.push(LoweringError::Other(
COMPARATOR_ERROR.into(),
));
}
}
_ => {
errors.push(LoweringError::Other(COMPARATOR_ERROR.into()));
}
}
}
}
}
SpecialMethod::Iterator => { if special_method_presence.iterator.is_some() {
errors.push(LoweringError::Other( "Cannot mark type as iterator twice".into(),
));
}
check_param_count("Iterator", 0, errors); // In theory we could support struct and enum iterators. The benefit is slight: // it generates probably inefficient code whilst being rather weird when it comes to the // "structs and enums convert across the boundary" norm for backends. // // Essentially, the `&mut self` behavior won't work right. // // Furthermore, in some backends (like Dart) defining an iterator may requiring adding fields, // which may not be possible for enums, and would still be an odd-one-out field for structs.g s
check_self_param("Iterator", true, errors); iflet Some(this) = &method.param_self { if !matches!(this.ty, SelfType::Opaque(..)) {
errors.push(LoweringError::Other( "Iterators only allowed on opaques".into(),
))
}
}
if method.output.success_type().is_unit() {
errors
.push(LoweringError::Other(format!("{name} must return a value")));
}
}
e @ (SpecialMethod::AddAssign
| SpecialMethod::SubAssign
| SpecialMethod::MulAssign
| SpecialMethod::DivAssign) => { let name = match e {
SpecialMethod::AddAssign => "AddAssign",
SpecialMethod::SubAssign => "SubAssign",
SpecialMethod::MulAssign => "MulAssign",
SpecialMethod::DivAssign => "DivAssign",
_ => unreachable!(),
};
check_param_count(name, 1, errors);
check_self_param(name, true, errors); iflet Some(self_param) = &method.param_self { if matches!(self_param.ty, SelfType::Struct(_) | SelfType::Enum(_)) {
errors.push(LoweringError::Other("*Assign arithmetic operations not allowed on non-opaque types. \ Use the non-mutating arithmetic operators instead".to_string()));
} elseif self_param.ty.is_immutably_borrowed() {
errors.push(LoweringError::Other(format!( "{name} must take self by mutable reference"
)));
}
} if !method.output.success_type().is_unit() {
errors.push(LoweringError::Other(format!( "{name} must not return a value"
)));
}
}
}
} else {
errors.push(LoweringError::Other(format!("Special method (type {special:?}) not allowed on non-method context {context:?}")))
}
}
if namespace.is_some()
&& matches!(
context,
AttributeContext::Method(..) | AttributeContext::EnumVariant(..)
)
{
errors.push(LoweringError::Other( "`namespace` can only be used on types".to_string(),
));
} if *default && !matches!(context, AttributeContext::EnumVariant(..)) {
errors.push(LoweringError::Other( "`default` can only be used on types and enum variants".to_string(),
));
} if matches!(
context,
AttributeContext::Param | AttributeContext::SelfParam | AttributeContext::Field
) { if *disable {
errors.push(LoweringError::Other(format!( "`disable`s cannot be used on an {context:?}."
)));
}
if namespace.is_some() {
errors.push(LoweringError::Other(format!( "`namespace` cannot be used on an {context:?}."
)));
}
if !rename.is_empty() || !abi_rename.is_empty() {
errors.push(LoweringError::Other(format!( "`rename`s cannot be used on an {context:?}."
)));
}
if special_method.is_some() {
errors.push(LoweringError::Other(format!( "{context:?} cannot be special methods."
)));
}
}
if *custom_errors
&& !matches!(
context,
AttributeContext::Type(..)
| AttributeContext::Trait(..)
| AttributeContext::Function(..)
)
{
errors.push(LoweringError::Other( "`error` can only be used on types".to_string(),
));
} if *generate_mocking_interface
&& !matches!(context, AttributeContext::Type(TypeDef::Opaque(..)))
{
errors.push(LoweringError::Other( "`generate_mocking_interface` can only be used on opaque types".to_string(),
));
}
if *abi_compatible && !matches!(context, AttributeContext::Type(TypeDef::Struct(..))) {
errors.push(LoweringError::Other( "`abi_compatible` can only be used on non-output-only struct types.".into(),
));
}
}
// Disabling shouldn't inherit to variants let disable = if context == AttrInheritContext::Variant { false
} else { self.disable
}; let namespace = if matches!(
context,
AttrInheritContext::Module | AttrInheritContext::Type
) { self.namespace.clone()
} else {
None
};
Attrs {
disable,
deprecated: None,
rename,
namespace, // Should not inherit from enums to their variants
default: false, // Was already inherited on the AST side
abi_rename: Default::default(), // Never inherited
special_method: None, // Not inherited
custom_errors: false,
demo_attrs: Default::default(), // Not inherited
generate_mocking_interface: false,
abi_compatible: false,
}
}
}
/// Non-exhaustive list of what attributes and other features your backend is able to handle, based on #[diplomat::attr(...)] contents. /// Set this through an [`AttributeValidator`]. /// /// See [`SpecialMethod`] and [`Attrs`] for your specific implementation needs. /// /// For example, the current dart backend supports [`BackendAttrSupport::constructors`]. So when it encounters: /// ```ignore /// struct Sample {} /// impl Sample { /// #[diplomat::attr(constructor)] /// pub fn new() -> Box<Self> { /// Box::new(Sample{}) /// } /// } /// /// ``` /// /// It generates /// ```dart /// factory Sample() /// ``` /// /// If a backend does not support a specific `#[diplomat::attr(...)]`, it may error. #[non_exhaustive] #[derive(Copy, Clone, Debug, Default)] pubstruct BackendAttrSupport { /// Namespacing types, e.g. C++ `namespace`. pub namespacing: bool, /// Rust can directly acccess the memory of this language, like C and C++. /// This is not supported in any garbage-collected language. pub memory_sharing: bool, /// This language's structs are non-exhaustive by default, i.e. adding /// fields is not a breaking change. pub non_exhaustive_structs: bool, /// Whether the language supports method overloading pub method_overloading: bool, /// Whether the language uses UTF-8 strings pub utf8_strings: bool, /// Whether the language uses UTF-16 strings pub utf16_strings: bool, /// Whether the language supports using slices with 'static lifetimes. pub static_slices: bool,
/// Whether the language supports marking types as having a default value pub defaults: bool,
// Special methods /// Marking a method as a constructor to generate special constructor methods. pub constructors: bool, /// Marking a method as a named constructor to generate special named constructor methods. pub named_constructors: bool, /// Marking constructors as being able to return errors. This is possible in languages where /// errors are thrown as exceptions (Dart), but not for example in C++, where errors are /// returned as values (constructors usually have to return the type itself). pub fallible_constructors: bool, /// Marking methods as field getters and setters, see [`SpecialMethod::Getter`] and [`SpecialMethod::Setter`] pub accessors: bool, /// Marking *static* methods as field getters and setters, see [`SpecialMethod::Getter`] and [`SpecialMethod::Setter`] pub static_accessors: bool, /// Marking a method as the `to_string` method, which is special in this language. pub stringifiers: bool, /// Marking a method as the `compare_to` method, which is special in this language. pub comparators: bool, /// Marking a method as the `next` method, which is special in this language. pub iterators: bool, /// Marking a method as the `iterator` method, which is special in this language. pub iterables: bool, /// Marking a method as the `[]` operator, which is special in this language. pub indexing: bool, /// Marking a method as an arithmetic operator (+-*/[=]) pub arithmetic: bool, /// Support for Option<Struct> and Option<Primitive> pub option: bool, /// Allowing callback arguments pub callbacks: bool, /// Allowing traits pub traits: bool, /// Marking a user-defined type as being a valid error result type. pub custom_errors: bool, /// Traits are safe to Send between threads (safe to mark as std::marker::Send) pub traits_are_send: bool, /// Traits are safe to Sync between threads (safe to mark as std::marker::Sync) pub traits_are_sync: bool, /// Whether to generate mocking interface. pub generate_mocking_interface: bool, /// Passing of structs that only hold (non-slice) primitive types /// (for use in slices and languages that support taking direct pointers to structs): pub abi_compatibles: bool, /// Whether or not the language supports &Struct or &mut Struct pub struct_refs: bool, /// Whether the language supports generating functions not associated with any type. pub free_functions: bool,
}
/// Defined by backends when validating attributes pubtrait AttributeValidator { /// The primary name of the backend, for use in diagnostics fn primary_name(&self) -> &str; /// Does this backend satisfy `cfg(backend_name)`? /// (Backends are allowed to satisfy multiple backend names, useful when there /// are multiple backends for a language) fn is_backend(&self, backend_name: &str) -> bool; /// does this backend satisfy cfg(name = value)? fn is_name_value(&self, name: &str, value: &str) -> Result<bool, LoweringError>; /// What backedn attrs does this support? fn attrs_supported(&self) -> BackendAttrSupport;
/// Provided, checks if type satisfies a `DiplomatBackendAttrCfg` /// /// auto_found helps check for `auto`, which is only allowed within `any` and at the top level. When `None`, /// `auto` is not allowed. fn satisfies_cfg(
&self,
cfg: &DiplomatBackendAttrCfg, mut auto_found: Option<&mut bool>,
) -> Result<bool, LoweringError> {
Ok(match *cfg {
DiplomatBackendAttrCfg::Not(ref c) => !self.satisfies_cfg(c, None)?,
DiplomatBackendAttrCfg::Any(ref cs) => { #[allow(clippy::needless_option_as_deref)] // False positive: we need this for reborrowing for c in cs { ifself.satisfies_cfg(c, auto_found.as_deref_mut())? { return Ok(true);
}
} false
}
DiplomatBackendAttrCfg::All(ref cs) => { for c in cs { if !self.satisfies_cfg(c, None)? { return Ok(false);
}
} true
}
DiplomatBackendAttrCfg::Auto => { iflet Some(found) = auto_found {
*found = true; return Ok(true);
} else { return Err(LoweringError::Other("auto in diplomat::attr() is only allowed at the top level and within `any`".into()));
}
}
DiplomatBackendAttrCfg::Star => true,
DiplomatBackendAttrCfg::BackendName(ref n) => self.is_backend(n),
DiplomatBackendAttrCfg::NameValue(ref n, ref v) => self.is_name_value(n, v)?,
})
}
// Provided: validates an attribute in the context in which it was constructed fn validate(&self, attrs: &Attrs, context: AttributeContext, errors: &mut ErrorStore) {
attrs.validate(self, context, errors)
}
}
/// A basic attribute validator #[non_exhaustive] #[derive(Default)] pubstruct BasicAttributeValidator { /// The primary name of this backend (should be unique, ideally) pub backend_name: String, /// The attributes supported pub support: BackendAttrSupport, /// Additional names for this backend pub other_backend_names: Vec<String>, /// override is_name_value() #[allow(clippy::type_complexity)] // dyn fn is not that complex pub is_name_value: Option<Box<dynFn(&str, &str) -> bool>>,
}
#[test] fn test_unsupported_features() {
uitest_lowering_attr! { hir::BackendAttrSupport::default(), #[diplomat::bridge] mod ffi { use std::cmp; use diplomat_runtime::DiplomatOption;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.