use std::{
cmp::{Ord, Ordering, PartialOrd},
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
fmt,
iter::Iterator,
ops::{Deref, DerefMut},
str::FromStr,
};
use crate::{
analysis::conversion_type::ConversionType, config::gobjects::GStatus, env::Env,
nameutil::split_namespace_name, traits::*, version::Version,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Transfer {
None,
Container,
Full,
}
impl FromStr for Transfer {
type Err = String;
fn from_str(name: &str) -> Result<Self, String> {
match name {
"none" => Ok(Self::None),
"container" => Ok(Self::Container),
"full" => Ok(Self::Full),
_ => Err(format!("Unknown ownership transfer mode '{name}'")),
}
}
}
#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
pub enum ParameterDirection {
None,
#[default]
In,
Out,
InOut,
Return,
}
impl ParameterDirection {
pub fn is_in(self) -> bool {
matches!(self, Self::In | Self::InOut)
}
pub fn is_out(self) -> bool {
matches!(self, Self::Out | Self::InOut)
}
}
impl FromStr for ParameterDirection {
type Err = String;
fn from_str(name: &str) -> Result<Self, String> {
match name {
"in" => Ok(Self::In),
"out" => Ok(Self::Out),
"inout" => Ok(Self::InOut),
_ => Err(format!("Unknown parameter direction '{name}'")),
}
}
}
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
pub enum ParameterScope {
#[default]
None,
Call,
Async,
Notified,
Forever,
}
impl ParameterScope {
pub fn is_forever(self) -> bool {
matches!(self, Self::Forever)
}
pub fn is_call(self) -> bool {
matches!(self, Self::Call)
}
pub fn is_async(self) -> bool {
matches!(self, Self::Async)
}
pub fn is_none(self) -> bool {
matches!(self, Self::None)
}
}
impl FromStr for ParameterScope {
type Err = String;
fn from_str(name: &str) -> Result<Self, String> {
match name {
"call" => Ok(Self::Call),
"async" => Ok(Self::Async),
"notified" => Ok(Self::Notified),
"forever" => Ok(Self::Forever),
_ => Err(format!("Unknown parameter scope type: {name}")),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Nullable(pub bool);
impl Deref for Nullable {
type Target = bool;
fn deref(&self) -> &bool {
&self.0
}
}
impl DerefMut for Nullable {
fn deref_mut(&mut self) -> &mut bool {
&mut self.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Mandatory(pub bool);
impl Deref for Mandatory {
type Target = bool;
fn deref(&self) -> &bool {
&self.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Infallible(pub bool);
impl Deref for Infallible {
type Target = bool;
fn deref(&self) -> &bool {
&self.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FunctionKind {
Constructor,
Function,
Method,
Global,
ClassMethod,
VirtualMethod,
}
impl FromStr for FunctionKind {
type Err = String;
fn from_str(name: &str) -> Result<Self, String> {
match name {
"constructor" => Ok(Self::Constructor),
"function" => Ok(Self::Function),
"method" => Ok(Self::Method),
"callback" => Ok(Self::Function),
"global" => Ok(Self::Global),
_ => Err(format!("Unknown function kind '{name}'")),
}
}
}
#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
pub enum Concurrency {
#[default]
None,
Send,
SendSync,
}
impl FromStr for Concurrency {
type Err = String;
fn from_str(name: &str) -> Result<Self, String> {
match name {
"none" => Ok(Self::None),
"send" => Ok(Self::Send),
"send+sync" => Ok(Self::SendSync),
_ => Err(format!("Unknown concurrency kind '{name}'")),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Basic {
None,
Boolean,
Int8,
UInt8,
Int16,
UInt16,
Int32,
UInt32,
Int64,
UInt64,
Char,
UChar,
Short,
UShort,
Int,
UInt,
Long,
ULong,
Size,
SSize,
Float,
Double,
Pointer,
VarArgs,
UniChar,
Utf8,
Filename,
Type,
IntPtr,
UIntPtr,
TimeT,
OffT,
DevT,
GidT,
PidT,
SockLenT,
UidT,
OsString,
Bool,
Unsupported,
}
impl Basic {
pub fn requires_conversion(&self) -> bool {
!matches!(
self,
Self::Int8
| Self::UInt8
| Self::Int16
| Self::UInt16
| Self::Int32
| Self::UInt32
| Self::Int64
| Self::UInt64
| Self::Char
| Self::UChar
| Self::Short
| Self::UShort
| Self::Int
| Self::UInt
| Self::Long
| Self::ULong
| Self::Size
| Self::SSize
| Self::Float
| Self::Double
| Self::Bool
)
}
}
const BASIC: &[(&str, Basic)] = &[
("none", Basic::None),
("gboolean", Basic::Boolean),
("gint8", Basic::Int8),
("guint8", Basic::UInt8),
("gint16", Basic::Int16),
("guint16", Basic::UInt16),
("gint32", Basic::Int32),
("guint32", Basic::UInt32),
("gint64", Basic::Int64),
("guint64", Basic::UInt64),
("gchar", Basic::Char),
("guchar", Basic::UChar),
("gshort", Basic::Short),
("gushort", Basic::UShort),
("gint", Basic::Int),
("guint", Basic::UInt),
("glong", Basic::Long),
("gulong", Basic::ULong),
("gsize", Basic::Size),
("gssize", Basic::SSize),
("gfloat", Basic::Float),
("gdouble", Basic::Double),
("long double", Basic::Unsupported),
("gunichar", Basic::UniChar),
("gconstpointer", Basic::Pointer),
("gpointer", Basic::Pointer),
("va_list", Basic::Unsupported),
("varargs", Basic::VarArgs),
("utf8", Basic::Utf8),
("filename", Basic::Filename),
("GType", Basic::Type),
("gintptr", Basic::IntPtr),
("guintptr", Basic::UIntPtr),
("os_string", Basic::OsString),
("bool", Basic::Bool),
("time_t", Basic::TimeT),
("off_t", Basic::OffT),
("dev_t", Basic::DevT),
("gid_t", Basic::GidT),
("pid_t", Basic::PidT),
("socklen_t", Basic::SockLenT),
("uid_t", Basic::UidT),
];
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct TypeId {
pub ns_id: u16,
pub id: u32,
}
impl TypeId {
pub fn full_name(self, library: &Library) -> String {
let ns_name = &library.namespace(self.ns_id).name;
let type_ = &library.type_(self);
format!("{}.{}", ns_name, &type_.get_name())
}
pub fn tid_none() -> TypeId {
Default::default()
}
pub fn tid_bool() -> TypeId {
TypeId { ns_id: 0, id: 1 }
}
pub fn tid_uint32() -> TypeId {
TypeId { ns_id: 0, id: 7 }
}
pub fn tid_utf8() -> TypeId {
TypeId { ns_id: 0, id: 28 }
}
pub fn tid_filename() -> TypeId {
TypeId { ns_id: 0, id: 29 }
}
pub fn tid_os_string() -> TypeId {
TypeId { ns_id: 0, id: 33 }
}
pub fn tid_c_bool() -> TypeId {
TypeId { ns_id: 0, id: 34 }
}
pub fn is_basic_type(self, env: &Env) -> bool {
env.library.type_(self).is_basic_type(env)
}
}
#[derive(Debug)]
pub struct Alias {
pub name: String,
pub c_identifier: String,
pub typ: TypeId,
pub target_c_type: String,
pub doc: Option<String>,
pub doc_deprecated: Option<String>,
}
#[derive(Debug)]
pub struct Constant {
pub name: String,
pub c_identifier: String,
pub typ: TypeId,
pub c_type: String,
pub value: String,
pub version: Option<Version>,
pub deprecated_version: Option<Version>,
pub doc: Option<String>,
pub doc_deprecated: Option<String>,
}
#[derive(Debug)]
pub struct Member {
pub name: String,
pub c_identifier: String,
pub value: String,
pub doc: Option<String>,
pub doc_deprecated: Option<String>,
pub status: GStatus,
pub version: Option<Version>,
pub deprecated_version: Option<Version>,
}
#[derive(Debug)]
pub enum ErrorDomain {
Quark(String),
Function(String),
}
#[derive(Debug)]
pub struct Enumeration {
pub name: String,
pub c_type: String,
pub symbol_prefix: Option<String>,
pub members: Vec<Member>,
pub functions: Vec<Function>,
pub version: Option<Version>,
pub deprecated_version: Option<Version>,
pub doc: Option<String>,
pub doc_deprecated: Option<String>,
pub error_domain: Option<ErrorDomain>,
pub glib_get_type: Option<String>,
}
#[derive(Debug)]
pub struct Bitfield {
pub name: String,
pub c_type: String,
pub symbol_prefix: Option<String>,
pub members: Vec<Member>,
pub functions: Vec<Function>,
pub version: Option<Version>,
pub deprecated_version: Option<Version>,
pub doc: Option<String>,
pub doc_deprecated: Option<String>,
pub glib_get_type: Option<String>,
}
#[derive(Default, Debug)]
pub struct Record {
pub name: String,
pub c_type: String,
pub symbol_prefix: Option<String>,
pub glib_get_type: Option<String>,
pub gtype_struct_for: Option<String>,
pub fields: Vec<Field>,
pub functions: Vec<Function>,
pub version: Option<Version>,
pub deprecated_version: Option<Version>,
pub doc: Option<String>,
pub doc_deprecated: Option<String>,
pub pointer: bool,
pub disguised: bool,
}
impl Record {
pub fn has_free(&self) -> bool {
self.functions.iter().any(|f| f.name == "free") || (self.has_copy() && self.has_destroy())
}
pub fn has_copy(&self) -> bool {
self.functions
.iter()
.any(|f| f.name == "copy" || f.name == "copy_into")
}
pub fn has_destroy(&self) -> bool {
self.functions.iter().any(|f| f.name == "destroy")
}
pub fn has_unref(&self) -> bool {
self.functions.iter().any(|f| f.name == "unref")
}
pub fn has_ref(&self) -> bool {
self.functions.iter().any(|f| f.name == "ref")
}
}
#[derive(Default, Debug)]
pub struct Field {
pub name: String,
pub typ: TypeId,
pub c_type: Option<String>,
pub private: bool,
pub bits: Option<u8>,
pub array_length: Option<u32>,
pub doc: Option<String>,
}
#[derive(Default, Debug)]
pub struct Union {
pub name: String,
pub c_type: Option<String>,
pub symbol_prefix: Option<String>,
pub glib_get_type: Option<String>,
pub fields: Vec<Field>,
pub functions: Vec<Function>,
pub doc: Option<String>,
}
#[derive(Debug)]
pub struct Property {
pub name: String,
pub readable: bool,
pub writable: bool,
pub construct: bool,
pub construct_only: bool,
pub typ: TypeId,
pub c_type: Option<String>,
pub transfer: Transfer,
pub version: Option<Version>,
pub deprecated_version: Option<Version>,
pub doc: Option<String>,
pub doc_deprecated: Option<String>,
pub getter: Option<String>,
pub setter: Option<String>,
}
#[derive(Clone, Debug)]
pub struct Parameter {
pub name: String,
pub typ: TypeId,
pub c_type: String,
pub instance_parameter: bool,
pub direction: ParameterDirection,
pub transfer: Transfer,
pub caller_allocates: bool,
pub nullable: Nullable,
pub array_length: Option<u32>,
pub is_error: bool,
pub doc: Option<String>,
pub scope: ParameterScope,
pub closure: Option<usize>,
pub destroy: Option<usize>,
}
#[derive(Debug)]
pub struct Function {
pub name: String,
pub c_identifier: Option<String>,
pub kind: FunctionKind,
pub parameters: Vec<Parameter>,
pub ret: Parameter,
pub throws: bool,
pub version: Option<Version>,
pub deprecated_version: Option<Version>,
pub doc: Option<String>,
pub doc_deprecated: Option<String>,
pub get_property: Option<String>,
pub set_property: Option<String>,
pub finish_func: Option<String>,
pub async_func: Option<String>,
pub sync_func: Option<String>,
}
#[derive(Debug)]
pub struct Signal {
pub name: String,
pub parameters: Vec<Parameter>,
pub ret: Parameter,
pub is_action: bool,
pub is_detailed: bool,
pub version: Option<Version>,
pub deprecated_version: Option<Version>,
pub doc: Option<String>,
pub doc_deprecated: Option<String>,
}
#[derive(Default, Debug)]
pub struct Interface {
pub name: String,
pub c_type: String,
pub symbol_prefix: String,
pub type_struct: Option<String>,
pub c_class_type: Option<String>,
pub glib_get_type: String,
pub functions: Vec<Function>,
pub virtual_methods: Vec<Function>,
pub signals: Vec<Signal>,
pub properties: Vec<Property>,
pub prerequisites: Vec<TypeId>,
pub version: Option<Version>,
pub deprecated_version: Option<Version>,
pub doc: Option<String>,
pub doc_deprecated: Option<String>,
}
#[derive(Default, Debug)]
pub struct Class {
pub name: String,
pub c_type: String,
pub symbol_prefix: String,
pub type_struct: Option<String>,
pub c_class_type: Option<String>,
pub glib_get_type: String,
pub fields: Vec<Field>,
pub functions: Vec<Function>,
pub virtual_methods: Vec<Function>,
pub signals: Vec<Signal>,
pub properties: Vec<Property>,
pub parent: Option<TypeId>,
pub implements: Vec<TypeId>,
pub final_type: bool,
pub version: Option<Version>,
pub deprecated_version: Option<Version>,
pub doc: Option<String>,
pub doc_deprecated: Option<String>,
pub is_abstract: bool,
pub is_fundamental: bool,
pub ref_fn: Option<String>,
pub unref_fn: Option<String>,
}
#[derive(Debug)]
pub struct Custom {
pub name: String,
pub conversion_type: ConversionType,
}
macro_rules! impl_lexical_ord {
() => ();
($name:ident => $field:ident, $($more_name:ident => $more_field:ident,)*) => (
impl_lexical_ord!($($more_name => $more_field,)*);
impl PartialEq for $name {
fn eq(&self, other: &$name) -> bool {
self.$field.eq(&other.$field)
}
}
impl Eq for $name { }
impl PartialOrd for $name {
fn partial_cmp(&self, other: &$name) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for $name {
fn cmp(&self, other: &$name) -> Ordering {
self.$field.cmp(&other.$field)
}
}
);
}
impl_lexical_ord!(
Alias => c_identifier,
Bitfield => c_type,
Class => c_type,
Enumeration => c_type,
Function => c_identifier,
Interface => c_type,
Record => c_type,
Union => c_type,
Custom => name,
);
#[derive(Debug, Eq, PartialEq)]
pub enum Type {
Basic(Basic),
Alias(Alias),
Enumeration(Enumeration),
Bitfield(Bitfield),
Record(Record),
Union(Union),
Function(Function),
Interface(Interface),
Class(Class),
Custom(Custom),
Array(TypeId),
CArray(TypeId),
FixedArray(TypeId, u16, Option<String>),
PtrArray(TypeId),
HashTable(TypeId, TypeId),
List(TypeId),
SList(TypeId),
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Basic(_) => "Basic",
Self::Alias(_) => "Alias",
Self::Enumeration(_) => "Enumeration",
Self::Bitfield(_) => "Bitfield",
Self::Record(_) => "Record",
Self::Union(_) => "Union",
Self::Function(_) => "Function",
Self::Interface(_) => "Interface",
Self::Class(_) => "Class",
Self::Custom(_) => "Custom",
Self::Array(_) => "Array",
Self::CArray(_) => "CArray",
Self::FixedArray(_, _, _) => "FixedArray",
Self::PtrArray(_) => "PtrArray",
Self::HashTable(_, _) => "HashTable",
Self::List(_) => "List",
Self::SList(_) => "SList",
})
}
}
impl Type {
pub fn get_name(&self) -> String {
match self {
Self::Basic(basic) => format!("{basic:?}"),
Self::Alias(alias) => alias.name.clone(),
Self::Enumeration(enum_) => enum_.name.clone(),
Self::Bitfield(bit_field) => bit_field.name.clone(),
Self::Record(rec) => rec.name.clone(),
Self::Union(u) => u.name.clone(),
Self::Function(func) => func.name.clone(),
Self::Interface(interface) => interface.name.clone(),
Self::Array(type_id) => format!("Array {type_id:?}"),
Self::Class(class) => class.name.clone(),
Self::Custom(custom) => custom.name.clone(),
Self::CArray(type_id) => format!("CArray {type_id:?}"),
Self::FixedArray(type_id, size, _) => format!("FixedArray {type_id:?}; {size}"),
Self::PtrArray(type_id) => format!("PtrArray {type_id:?}"),
Self::HashTable(key_type_id, value_type_id) => {
format!("HashTable {key_type_id:?}/{value_type_id:?}")
}
Self::List(type_id) => format!("List {type_id:?}"),
Self::SList(type_id) => format!("SList {type_id:?}"),
}
}
pub fn get_deprecated_version(&self) -> Option<Version> {
match self {
Self::Basic(_) => None,
Self::Alias(_) => None,
Self::Enumeration(enum_) => enum_.deprecated_version,
Self::Bitfield(bit_field) => bit_field.deprecated_version,
Self::Record(rec) => rec.deprecated_version,
Self::Union(_) => None,
Self::Function(func) => func.deprecated_version,
Self::Interface(interface) => interface.deprecated_version,
Self::Array(_) => None,
Self::Class(class) => class.deprecated_version,
Self::Custom(_) => None,
Self::CArray(_) => None,
Self::FixedArray(..) => None,
Self::PtrArray(_) => None,
Self::HashTable(_, _) => None,
Self::List(_) => None,
Self::SList(_) => None,
}
}
pub fn get_glib_name(&self) -> Option<&str> {
match self {
Self::Alias(alias) => Some(&alias.c_identifier),
Self::Enumeration(enum_) => Some(&enum_.c_type),
Self::Bitfield(bit_field) => Some(&bit_field.c_type),
Self::Record(rec) => Some(&rec.c_type),
Self::Union(union) => union.c_type.as_deref(),
Self::Function(func) => func.c_identifier.as_deref(),
Self::Interface(interface) => Some(&interface.c_type),
Self::Class(class) => Some(&class.c_type),
_ => None,
}
}
pub fn c_array(
library: &mut Library,
inner: TypeId,
size: Option<u16>,
c_type: Option<String>,
) -> TypeId {
let name = Self::c_array_internal_name(inner, size, &c_type);
if let Some(size) = size {
library.add_type(
INTERNAL_NAMESPACE,
&name,
Self::FixedArray(inner, size, c_type),
)
} else {
library.add_type(INTERNAL_NAMESPACE, &name, Self::CArray(inner))
}
}
pub fn find_c_array(library: &Library, inner: TypeId, size: Option<u16>) -> TypeId {
let name = Self::c_array_internal_name(inner, size, &None);
library
.find_type(INTERNAL_NAMESPACE, &name)
.unwrap_or_else(|| panic!("No type for '*.{name}'"))
}
fn c_array_internal_name(inner: TypeId, size: Option<u16>, c_type: &Option<String>) -> String {
if let Some(size) = size {
format!("[#{inner:?}; {size};{c_type:?}]")
} else {
format!("[#{inner:?}]")
}
}
pub fn container(library: &mut Library, name: &str, mut inner: Vec<TypeId>) -> Option<TypeId> {
match (name, inner.len()) {
("GLib.Array", 1) => {
let tid = inner.remove(0);
Some((format!("Array(#{tid:?})"), Self::Array(tid)))
}
("GLib.PtrArray", 1) => {
let tid = inner.remove(0);
Some((format!("PtrArray(#{tid:?})"), Self::PtrArray(tid)))
}
("GLib.HashTable", 2) => {
let k_tid = inner.remove(0);
let v_tid = inner.remove(0);
Some((
format!("HashTable(#{k_tid:?}, #{v_tid:?})"),
Self::HashTable(k_tid, v_tid),
))
}
("GLib.List", 1) => {
let tid = inner.remove(0);
Some((format!("List(#{tid:?})"), Self::List(tid)))
}
("GLib.SList", 1) => {
let tid = inner.remove(0);
Some((format!("SList(#{tid:?})"), Self::SList(tid)))
}
_ => None,
}
.map(|(name, typ)| library.add_type(INTERNAL_NAMESPACE, &name, typ))
}
pub fn function(library: &mut Library, func: Function) -> TypeId {
let mut param_tids: Vec<TypeId> = func.parameters.iter().map(|p| p.typ).collect();
param_tids.push(func.ret.typ);
let typ = Self::Function(func);
library.add_type(INTERNAL_NAMESPACE, &format!("fn<#{param_tids:?}>"), typ)
}
pub fn union(library: &mut Library, u: Union, ns_id: u16) -> TypeId {
let field_tids: Vec<TypeId> = u.fields.iter().map(|f| f.typ).collect();
let typ = Self::Union(u);
library.add_type(ns_id, &format!("#{field_tids:?}"), typ)
}
pub fn record(library: &mut Library, r: Record, ns_id: u16) -> TypeId {
let field_tids: Vec<TypeId> = r.fields.iter().map(|f| f.typ).collect();
let typ = Self::Record(r);
library.add_type(ns_id, &format!("#{field_tids:?}"), typ)
}
pub fn functions(&self) -> &[Function] {
match self {
Self::Enumeration(e) => &e.functions,
Self::Bitfield(b) => &b.functions,
Self::Record(r) => &r.functions,
Self::Union(u) => &u.functions,
Self::Interface(i) => &i.functions,
Self::Class(c) => &c.functions,
_ => &[],
}
}
pub fn is_basic(&self) -> bool {
matches!(*self, Self::Basic(_))
}
pub fn is_basic_type(&self, env: &Env) -> bool {
match self {
Self::Alias(x) => env.library.type_(x.typ).is_basic_type(env),
x => x.is_basic(),
}
}
pub fn get_inner_type<'a>(&'a self, env: &'a Env) -> Option<(&'a Type, u16)> {
match *self {
Self::Array(t)
| Self::CArray(t)
| Self::FixedArray(t, ..)
| Self::PtrArray(t)
| Self::List(t)
| Self::SList(t) => {
let ty = env.type_(t);
ty.get_inner_type(env).or(Some((ty, t.ns_id)))
}
_ => None,
}
}
pub fn is_function(&self) -> bool {
matches!(*self, Self::Function(_))
}
pub fn is_class(&self) -> bool {
matches!(*self, Self::Class(_))
}
pub fn is_interface(&self) -> bool {
matches!(*self, Self::Interface(_))
}
pub fn is_final_type(&self) -> bool {
match *self {
Self::Class(Class { final_type, .. }) => final_type,
Self::Interface(..) => false,
_ => true,
}
}
pub fn is_fundamental(&self) -> bool {
match *self {
Self::Class(Class { is_fundamental, .. }) => is_fundamental,
_ => false,
}
}
pub fn is_abstract(&self) -> bool {
match *self {
Self::Class(Class { is_abstract, .. }) => is_abstract,
_ => false,
}
}
pub fn is_enumeration(&self) -> bool {
matches!(*self, Self::Enumeration(_))
}
pub fn is_bitfield(&self) -> bool {
matches!(*self, Self::Bitfield(_))
}
}
macro_rules! impl_maybe_ref {
() => ();
($name:ident, $($more:ident,)*) => (
impl_maybe_ref!($($more,)*);
impl MaybeRef<$name> for Type {
fn maybe_ref(&self) -> Option<&$name> {
if let Self::$name(x) = self { Some(x) } else { None }
}
fn to_ref(&self) -> &$name {
self.maybe_ref().unwrap_or_else(|| {
panic!("{} is not a {}", self.get_name(), stringify!($name))
})
}
}
);
}
impl_maybe_ref!(
Alias,
Bitfield,
Class,
Enumeration,
Function,
Basic,
Interface,
Record,
Union,
);
impl<U> MaybeRefAs for U {
fn maybe_ref_as<T>(&self) -> Option<&T>
where
Self: MaybeRef<T>,
{
self.maybe_ref()
}
fn to_ref_as<T>(&self) -> &T
where
Self: MaybeRef<T>,
{
self.to_ref()
}
}
#[derive(Debug, Default)]
pub struct Namespace {
pub name: String,
pub types: Vec<Option<Type>>,
pub index: BTreeMap<String, u32>,
pub glib_name_index: HashMap<String, u32>,
pub constants: Vec<Constant>,
pub functions: Vec<Function>,
pub package_names: Vec<String>,
pub versions: BTreeSet<Version>,
pub doc: Option<String>,
pub doc_deprecated: Option<String>,
pub shared_library: Vec<String>,
pub identifier_prefixes: Vec<String>,
pub symbol_prefixes: Vec<String>,
pub c_includes: Vec<String>,
}
impl Namespace {
fn new(name: &str) -> Self {
Self {
name: name.into(),
..Self::default()
}
}
fn add_constant(&mut self, c: Constant) {
self.constants.push(c);
}
fn add_function(&mut self, f: Function) {
self.functions.push(f);
}
fn type_(&self, id: u32) -> &Type {
self.types[id as usize].as_ref().unwrap()
}
fn type_mut(&mut self, id: u32) -> &mut Type {
self.types[id as usize].as_mut().unwrap()
}
fn add_type(&mut self, name: &str, typ: Option<Type>) -> u32 {
let glib_name = typ
.as_ref()
.and_then(Type::get_glib_name)
.map(ToOwned::to_owned);
let id = if let Some(id) = self.find_type(name) {
self.types[id as usize] = typ;
id
} else {
let id = self.types.len() as u32;
self.types.push(typ);
self.index.insert(name.into(), id);
id
};
if let Some(s) = glib_name {
self.glib_name_index.insert(s, id);
}
id
}
fn find_type(&self, name: &str) -> Option<u32> {
self.index.get(name).copied()
}
}
pub const INTERNAL_NAMESPACE_NAME: &str = "*";
pub const INTERNAL_NAMESPACE: u16 = 0;
pub const MAIN_NAMESPACE: u16 = 1;
#[derive(Debug)]
pub struct Library {
pub namespaces: Vec<Namespace>,
pub index: HashMap<String, u16>,
}
impl Library {
pub fn new(main_namespace_name: &str) -> Self {
let mut library = Self {
namespaces: Vec::new(),
index: HashMap::new(),
};
assert_eq!(
INTERNAL_NAMESPACE,
library.add_namespace(INTERNAL_NAMESPACE_NAME)
);
for &(name, t) in BASIC {
library.add_type(INTERNAL_NAMESPACE, name, Type::Basic(t));
}
assert_eq!(MAIN_NAMESPACE, library.add_namespace(main_namespace_name));
Type::c_array(&mut library, TypeId::tid_utf8(), None, None);
Type::c_array(&mut library, TypeId::tid_filename(), None, None);
Type::c_array(&mut library, TypeId::tid_os_string(), None, None);
library
}
pub fn show_non_bound_types(&self, env: &Env) {
let not_allowed_ending = [
"Class",
"Private",
"Func",
"Callback",
"Accessible",
"Iface",
"Type",
"Interface",
];
let namespace_name = self.namespaces[MAIN_NAMESPACE as usize].name.clone();
let mut parents = HashSet::new();
for x in self.namespace(MAIN_NAMESPACE).types.iter().flatten() {
let name = x.get_name();
let full_name = format!("{namespace_name}.{name}");
let mut check_methods = true;
if !not_allowed_ending.iter().any(|s| name.ends_with(s))
|| x.is_enumeration()
|| x.is_bitfield()
{
let version = x.get_deprecated_version();
let depr_version = version.unwrap_or(env.config.min_cfg_version);
if !env.analysis.objects.contains_key(&full_name)
&& !env.analysis.records.contains_key(&full_name)
&& !env.config.objects.iter().any(|o| o.1.name == full_name)
&& depr_version >= env.config.min_cfg_version
{
check_methods = false;
if let Some(version) = version {
println!("[NOT GENERATED] {full_name} (deprecated in {version})");
} else {
println!("[NOT GENERATED] {full_name}");
}
} else if let Type::Class(Class { properties, .. }) = x {
if !env
.config
.objects
.get(&full_name)
.is_some_and(|obj| obj.generate_builder)
&& properties
.iter()
.any(|prop| prop.construct_only || prop.construct || prop.writable)
{
println!("[NOT GENERATED BUILDER] {full_name}Builder");
}
}
}
if let (Some(tid), Some(gobject_id)) = (
env.library.find_type(0, &full_name),
env.library.find_type(0, "GObject.Object"),
) {
for &super_tid in env.class_hierarchy.supertypes(tid) {
let ty = env.library.type_(super_tid);
let ns_id = super_tid.ns_id as usize;
let full_parent_name =
format!("{}.{}", self.namespaces[ns_id].name, ty.get_name());
if super_tid != gobject_id
&& env
.type_status(&super_tid.full_name(&env.library))
.ignored()
&& parents.insert(full_parent_name.clone())
{
if let Some(version) = ty.get_deprecated_version() {
println!(
"[NOT GENERATED PARENT] {full_parent_name} (deprecated in {version})"
);
} else {
println!("[NOT GENERATED PARENT] {full_parent_name}");
}
}
}
if check_methods {
self.not_bound_functions(
env,
&format!("{full_name}::"),
x.functions(),
"METHOD",
);
}
}
}
self.not_bound_functions(
env,
&format!("{namespace_name}."),
&self.namespace(MAIN_NAMESPACE).functions,
"FUNCTION",
);
}
fn not_bound_functions(&self, env: &Env, prefix: &str, functions: &[Function], kind: &str) {
for func in functions {
let version = func.deprecated_version;
let depr_version = version.unwrap_or(env.config.min_cfg_version);
if depr_version < env.config.min_cfg_version {
continue;
}
let mut errors = func
.parameters
.iter()
.filter_map(|p| {
let mut ty = env.library.type_(p.typ);
let mut ns_id = p.typ.ns_id as usize;
if let Some((t, n)) = ty.get_inner_type(env) {
ty = t;
ns_id = n as usize;
}
if ty.is_basic() {
return None;
}
let full_name = format!("{}.{}", self.namespaces[ns_id].name, ty.get_name());
if env.type_status(&p.typ.full_name(&env.library)).ignored()
&& !env.analysis.objects.contains_key(&full_name)
&& !env.analysis.records.contains_key(&full_name)
&& !env.config.objects.iter().any(|o| o.1.name == full_name)
{
Some(full_name)
} else {
None
}
})
.collect::<Vec<_>>();
{
let mut ty = env.library.type_(func.ret.typ);
let mut ns_id = func.ret.typ.ns_id as usize;
if let Some((t, n)) = ty.get_inner_type(env) {
ty = t;
ns_id = n as usize;
}
if !ty.is_basic() {
let full_name = format!("{}.{}", self.namespaces[ns_id].name, ty.get_name());
if env
.type_status(&func.ret.typ.full_name(&env.library))
.ignored()
&& !env.analysis.objects.contains_key(&full_name)
&& !env.analysis.records.contains_key(&full_name)
&& !env.config.objects.iter().any(|o| o.1.name == full_name)
{
errors.push(full_name);
}
}
}
if !errors.is_empty() {
let full_name = format!("{}{}", prefix, func.name);
let deprecated_version = match version {
Some(dv) => format!(" (deprecated in {dv})"),
None => String::new(),
};
if errors.len() > 1 {
let end = errors.pop().unwrap();
let begin = errors.join(", ");
println!(
"[NOT GENERATED {kind}] {full_name}{deprecated_version} because of {begin} and {end}"
);
} else {
println!(
"[NOT GENERATED {}] {}{} because of {}",
kind, full_name, deprecated_version, errors[0]
);
}
}
}
}
pub fn namespace(&self, ns_id: u16) -> &Namespace {
&self.namespaces[ns_id as usize]
}
pub fn namespace_mut(&mut self, ns_id: u16) -> &mut Namespace {
&mut self.namespaces[ns_id as usize]
}
pub fn find_namespace(&self, name: &str) -> Option<u16> {
self.index.get(name).copied()
}
pub fn add_namespace(&mut self, name: &str) -> u16 {
if let Some(&id) = self.index.get(name) {
id
} else {
let id = self.namespaces.len() as u16;
self.namespaces.push(Namespace::new(name));
self.index.insert(name.into(), id);
id
}
}
pub fn add_constant(&mut self, ns_id: u16, c: Constant) {
self.namespace_mut(ns_id).add_constant(c);
}
pub fn add_function(&mut self, ns_id: u16, f: Function) {
self.namespace_mut(ns_id).add_function(f);
}
pub fn add_type(&mut self, ns_id: u16, name: &str, typ: Type) -> TypeId {
TypeId {
ns_id,
id: self.namespace_mut(ns_id).add_type(name, Some(typ)),
}
}
#[allow(clippy::manual_map)]
pub fn find_type(&self, current_ns_id: u16, name: &str) -> Option<TypeId> {
let (mut ns, name) = split_namespace_name(name);
if name == "GType" {
ns = None;
}
if let Some(ns) = ns {
self.find_namespace(ns).and_then(|ns_id| {
self.namespace(ns_id)
.find_type(name)
.map(|id| TypeId { ns_id, id })
})
} else if let Some(id) = self.namespace(current_ns_id).find_type(name) {
Some(TypeId {
ns_id: current_ns_id,
id,
})
} else if let Some(id) = self.namespace(INTERNAL_NAMESPACE).find_type(name) {
Some(TypeId {
ns_id: INTERNAL_NAMESPACE,
id,
})
} else {
None
}
}
pub fn find_or_stub_type(&mut self, current_ns_id: u16, name: &str) -> TypeId {
if let Some(tid) = self.find_type(current_ns_id, name) {
return tid;
}
let (ns, name) = split_namespace_name(name);
if let Some(ns) = ns {
let ns_id = self
.find_namespace(ns)
.unwrap_or_else(|| self.add_namespace(ns));
let ns = self.namespace_mut(ns_id);
let id = ns
.find_type(name)
.unwrap_or_else(|| ns.add_type(name, None));
return TypeId { ns_id, id };
}
let id = self.namespace_mut(current_ns_id).add_type(name, None);
TypeId {
ns_id: current_ns_id,
id,
}
}
pub fn type_(&self, tid: TypeId) -> &Type {
self.namespace(tid.ns_id).type_(tid.id)
}
pub fn type_mut(&mut self, tid: TypeId) -> &mut Type {
self.namespace_mut(tid.ns_id).type_mut(tid.id)
}
pub fn register_version(&mut self, ns_id: u16, version: Version) {
self.namespace_mut(ns_id).versions.insert(version);
}
pub fn types<'a>(&'a self) -> Box<dyn Iterator<Item = (TypeId, &'a Type)> + 'a> {
Box::new(self.namespaces.iter().enumerate().flat_map(|(ns_id, ns)| {
ns.types.iter().enumerate().filter_map(move |(id, type_)| {
let tid = TypeId {
ns_id: ns_id as u16,
id: id as u32,
};
type_.as_ref().map(|t| (tid, t))
})
}))
}
pub fn namespace_types<'a>(
&'a self,
ns_id: u16,
) -> Box<dyn Iterator<Item = (TypeId, &'a Type)> + 'a> {
let ns = self.namespace(ns_id);
Box::new(ns.index.values().map(move |&id| {
(
TypeId { ns_id, id },
ns.types[id as usize].as_ref().unwrap(),
)
}))
}
pub fn is_crate(&self, crate_name: &str) -> bool {
self.namespace(MAIN_NAMESPACE).name == crate_name
}
pub fn is_glib_crate(&self) -> bool {
self.is_crate("GObject") || self.is_crate("GLib")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_tids() {
let lib = Library::new("Gtk");
assert_eq!(TypeId::tid_none().full_name(&lib), "*.None");
assert_eq!(TypeId::tid_bool().full_name(&lib), "*.Boolean");
assert_eq!(TypeId::tid_uint32().full_name(&lib), "*.UInt32");
assert_eq!(TypeId::tid_c_bool().full_name(&lib), "*.Bool");
assert_eq!(TypeId::tid_utf8().full_name(&lib), "*.Utf8");
assert_eq!(TypeId::tid_filename().full_name(&lib), "*.Filename");
assert_eq!(TypeId::tid_os_string().full_name(&lib), "*.OsString");
}
}