Backport official GLib iterator safety fix for Linux desktop

This commit is contained in:
karl committed 2026-10-13 10:35:50 +02:00
1 parent 43c43ebdc9
commit 8ef874c609
133 files changed
+55342 -2

No files matched your search

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "42b9caf98e03ded086362d9653ca58fe94dc8658"
},
"path_in_vcs": "glib"
}
+14
View File
@@ -0,0 +1,14 @@
The gtk-rs Project is licensed under the MIT license, see the LICENSE file or
<http://opensource.org/licenses/MIT>.
Copyrights in the gtk-rs Project project are retained by their contributors.
No copyright assignment is required to contribute to the gtk-rs Project
project.
For full authorship information, see the version control history.
This project provides interoperability with various GNOME libraries but
doesn't distribute any parts of them. Distributing compiled libraries and
executables that link to those libraries may be subject to terms of the GNU
LGPL or other licenses. For more information check the license of each GNOME
library.
+185
View File
@@ -0,0 +1,185 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.70"
name = "glib"
version = "0.18.5"
authors = ["The gtk-rs Project Developers"]
exclude = ["gir-files/*"]
description = "Rust bindings for the GLib library"
homepage = "https://gtk-rs.org/"
documentation = "https://gtk-rs.org/gtk-rs-core/stable/latest/docs/glib/"
readme = "README.md"
keywords = [
"glib",
"gtk-rs",
"gnome",
"GUI",
]
license = "MIT"
repository = "https://github.com/gtk-rs/gtk-rs-core"
[package.metadata.docs.rs]
all-features = true
rustc-args = [
"--cfg",
"docsrs",
]
rustdoc-args = [
"--cfg",
"docsrs",
"--generate-link-to-definition",
]
[lib]
name = "glib"
[[test]]
name = "subclass_compiletest"
required-features = ["compiletests"]
[[bench]]
name = "gstring"
harness = false
[dependencies.bitflags]
version = "2.3"
[dependencies.ffi]
version = "0.18"
package = "glib-sys"
[dependencies.futures-channel]
version = "0.3"
[dependencies.futures-core]
version = "0.3"
default-features = false
[dependencies.futures-executor]
version = "0.3"
[dependencies.futures-task]
version = "0.3"
default-features = false
[dependencies.futures-util]
version = "0.3"
[dependencies.gio_ffi]
version = "0.18"
optional = true
package = "gio-sys"
[dependencies.glib-macros]
version = "0.18"
[dependencies.gobject_ffi]
version = "0.18"
package = "gobject-sys"
[dependencies.libc]
version = "0.2"
[dependencies.memchr]
version = "2.5.0"
[dependencies.once_cell]
version = "1.0"
[dependencies.rs-log]
version = "0.4"
optional = true
package = "log"
[dependencies.smallvec]
version = "1.0"
features = [
"union",
"const_generics",
"const_new",
]
[dependencies.thiserror]
version = "1"
[dev-dependencies.criterion]
version = "0.5.1"
[dev-dependencies.gir-format-check]
version = "^0.1"
[dev-dependencies.tempfile]
version = "3"
[dev-dependencies.trybuild2]
version = "1"
[features]
compiletests = []
default = ["gio"]
gio = ["gio_ffi"]
log = ["rs-log"]
log_macros = ["log"]
v2_58 = [
"ffi/v2_58",
"gobject_ffi/v2_58",
]
v2_60 = [
"v2_58",
"ffi/v2_60",
]
v2_62 = [
"v2_60",
"ffi/v2_62",
"gobject_ffi/v2_62",
]
v2_64 = [
"v2_62",
"ffi/v2_64",
]
v2_66 = [
"v2_64",
"ffi/v2_66",
"gobject_ffi/v2_66",
]
v2_68 = [
"v2_66",
"ffi/v2_68",
"gobject_ffi/v2_68",
]
v2_70 = [
"v2_68",
"ffi/v2_70",
"gobject_ffi/v2_70",
]
v2_72 = [
"v2_70",
"ffi/v2_72",
"gobject_ffi/v2_72",
]
v2_74 = [
"v2_72",
"ffi/v2_74",
"gobject_ffi/v2_74",
]
v2_76 = [
"v2_74",
"ffi/v2_76",
"gobject_ffi/v2_76",
]
v2_78 = [
"v2_76",
"ffi/v2_78",
"gobject_ffi/v2_78",
]
+74
View File
@@ -0,0 +1,74 @@
[package]
name = "glib"
documentation = "https://gtk-rs.org/gtk-rs-core/stable/latest/docs/glib/"
homepage = "https://gtk-rs.org/"
authors = ["The gtk-rs Project Developers"]
description = "Rust bindings for the GLib library"
readme = "README.md"
version = "0.18.5"
keywords = ["glib", "gtk-rs", "gnome", "GUI"]
repository = "https://github.com/gtk-rs/gtk-rs-core"
license = "MIT"
exclude = [
"gir-files/*",
]
edition = "2021"
rust-version = "1.70"
[lib]
name = "glib"
[dependencies]
once_cell = "1.0"
libc = "0.2"
bitflags = "2.3"
futures-core = { version = "0.3", default-features = false }
futures-task = { version = "0.3", default-features = false }
futures-executor = "0.3"
futures-channel = "0.3"
futures-util = "0.3"
ffi = { package = "glib-sys", version = "0.18", path = "sys" }
gobject_ffi = { package = "gobject-sys", version = "0.18", path = "gobject-sys" }
glib-macros = { path = "../glib-macros", version = "0.18" }
rs-log = { package = "log", version = "0.4", optional = true }
smallvec = { version = "1.0", features = ["union", "const_generics", "const_new"] }
thiserror = "1"
gio_ffi = { package = "gio-sys", path = "../gio/sys", version = "0.18", optional = true }
memchr = "2.5.0"
[dev-dependencies]
tempfile = "3"
gir-format-check = "^0.1"
trybuild2 = "1"
criterion = "0.5.1"
[features]
default = ["gio"]
v2_58 = ["ffi/v2_58", "gobject_ffi/v2_58"]
v2_60 = ["v2_58", "ffi/v2_60"]
v2_62 = ["v2_60", "ffi/v2_62", "gobject_ffi/v2_62"]
v2_64 = ["v2_62", "ffi/v2_64"]
v2_66 = ["v2_64", "ffi/v2_66", "gobject_ffi/v2_66"]
v2_68 = ["v2_66", "ffi/v2_68", "gobject_ffi/v2_68"]
v2_70 = ["v2_68", "ffi/v2_70", "gobject_ffi/v2_70"]
v2_72 = ["v2_70", "ffi/v2_72", "gobject_ffi/v2_72"]
v2_74 = ["v2_72", "ffi/v2_74", "gobject_ffi/v2_74"]
v2_76 = ["v2_74", "ffi/v2_76", "gobject_ffi/v2_76"]
v2_78 = ["v2_76", "ffi/v2_78", "gobject_ffi/v2_78"]
log = ["rs-log"]
log_macros = ["log"]
compiletests = []
gio = ["gio_ffi"]
[package.metadata.docs.rs]
all-features = true
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"]
[[test]]
name = "subclass_compiletest"
required-features = ["compiletests"]
[[bench]]
name = "gstring"
harness = false
+816
View File
@@ -0,0 +1,816 @@
[options]
girs_directories = ["../gir-files"]
library = "GLib"
version = "2.0"
min_cfg_version = "2.56"
target_path = "."
work_mode = "normal"
single_version_file = true
deprecate_by_min_version = true
trust_return_value_nullability = true
generate = [
"GLib.ConvertError",
"GLib.DateDay",
"GLib.DateMonth",
"GLib.DateWeekday",
"GLib.DateYear",
"GLib.FileError",
"GLib.FileSetContentsFlags",
"GLib.IOCondition",
"GLib.KeyFileError",
"GLib.KeyFileFlags",
"GLib.LogLevelFlags",
"GLib.LogWriterOutput",
"GLib.MainContextFlags",
"GLib.MarkupError",
"GLib.NormalizeMode",
"GLib.OptionArg",
"GLib.OptionFlags",
"GLib.SeekType",
"GLib.SpawnFlags",
"GLib.Time",
"GLib.TimeType",
"GLib.UnicodeType",
"GLib.UriError",
"GLib.UriFlags",
"GLib.UriHideFlags",
"GLib.UriParamsFlags",
"GLib.VariantClass",
]
manual = [
"GLib.ByteArray",
"GLib.Bytes",
"GLib.Error",
"GLib.UserDirectory",
"GLib.Variant",
"GLib.VariantType",
"GObject.Object",
]
[[object]]
name = "GLib.*"
status = "generate"
[[object.function]]
pattern = "(assertion_message_cmpint|assert_warning|assertion_message|assertion_message_cmpnum|assertion_message_cmpstr|warn_message|return_if_fail_warning)"
ignore = true # Not useful assertions functions
[[object.function]]
pattern = "(set_print_handler|set_printerr_handler|vasprintf|vfprintf|vprintf|vsnprintf|vsprintf|stpcpy|sprintf|snprintf|printf|printf_string_upper_bound|printerr|prefix_error|print|fprintf)"
ignore = true # Not useful string functions
[[object.function]]
pattern = "(malloc|malloc0|malloc0_n|malloc_n|memdup|memdup2|realloc|realloc_n|try_malloc||try_malloc0|try_malloc0_n|try_malloc_n|try_realloc|try_realloc_n|free|aligned_alloc|aligned_alloc0|aligned_free|free_sized|aligned_free_sized)"
ignore = true # Not useful allocations functions
[[object.function]]
pattern = "(slice_get_config|slice_get_config_state|slice_set_config|slice_alloc|slice_alloc0|slice_copy|slice_free1|slice_free_chain_with_offset)"
ignore = true # Not useful slices functions
[[object.function]]
pattern = "(path_is_absolute|path_skip_root|rmdir|basename|canonicalize_filename|build_filename|build_filename_valist|build_filenamev|build_path|build_pathv|canonicalize_filename|chdir)"
ignore = true # Not useful paths functions, use Path/PathBuf
[[object.function]]
pattern = "(int64_equal|int64_hash|int_equal|int_hash|double_equal|double_hash|direct_equal|direct_hash)"
ignore = true # Not useful equal/hash functions, only useful for GHashTable
[[object.function]]
pattern = "(clear_handle_id|clear_list|clear_pointer|clear_slist|clear_error|nullify_pointer)"
ignore = true # Not useful freeing functions
[[object.function]]
pattern = "(datalist_.*|dataset_.*)"
ignore = true # Not useful GData related functions
[[object.function]]
pattern = "(path_get_basename|path_get_dirname|file_test)"
visibility = "crate" # used for tests
[[object.function]]
pattern = "(propagate_prefixed_error|set_error|file_error_from_errno|propagate_error|set_error_literal|set_error)"
ignore = true # Not useful error handling functions
[[object.function]]
name = "parse_debug_string"
ignore = true # useless
[[object.function]]
pattern = "(log_.*|logv)"
ignore = true # needs manual wrappers to make their usage nicer
[[object.function]]
pattern = "compute_.*_for_string"
ignore = true # needs manual wrapper to avoid an unecessary copy of the string
[[object.function]]
pattern = "(idle_remove_by_data|qsort_with_data)"
ignore = true # Unsafe functions
[[object.function]]
pattern = "iconv"
doc_struct_name = "IConv"
manual = true
[[object.function]]
pattern = "convert_with_iconv"
doc_struct_name = "IConv"
manual = true
rename = "convert"
[[object.function]]
pattern = "pointer_.*"
ignore = true # Not usable, uses raw pointers
[[object.function]]
pattern = "file_read_link"
ignore = true # use the ones from the std
[[object.function]]
pattern = "pattern_.*"
ignore = true # use the ones from the std
[[object.function]]
pattern = "(markup_collect_attributes|markup_printf_escaped|markup_vprintf_escaped)"
ignore = true # varargs functions are unbindable
[[object.function]]
pattern = "io_.*"
ignore = true # they are supposed to be used with IOChannel, which doesn't have safe bindings
[[object.function]]
name = "file_open_tmp"
manual = true # Return a RawFd instead of a i32
[[object.function]]
pattern = "atomic_.+"
ignore = true
[[object.function]]
pattern = "bit_.*"
ignore = true # use the ones in the std
[[object.function]]
name = "get_current_time"
ignore = true # Deprecated
[[object.function]]
name = "get_filename_charsets"
manual = true
[[object.function]]
pattern = "str.+v"
#string
ignore = true
[[object.function]]
name = "strv_length"
#string
ignore = true
[[object.function]]
pattern = "str.+"
ignore = true
[[object.function]]
pattern = "ucs4_.+"
ignore = true
[[object.function]]
pattern = "utf16_.+"
ignore = true
[[object.function]]
pattern = "utf8_.+"
ignore = true
[[object.function]]
pattern = "unichar_.+"
ignore = true
[[object.function]]
pattern = "ascii_.+"
ignore = true
[[object.function]]
name = "assertion_message_error"
#Quark
ignore = true
[[object.function]]
name = "assertion_message_expr"
ignore = true # function is useless
[[object.function]]
name = "assertion_message_cmpstrv"
ignore = true # function is useless
[[object.function]]
pattern = "test_.+"
ignore = true # functions are useless
[[object.function]]
pattern = "prefix_error_literal"
ignore = true # functions are useless
[[object.function]]
pattern = ".+_error_quark"
#Quark
ignore = true
[[object.function]]
pattern = "quark_.+_string"
#Quark
ignore = true
[[object.function]]
name = "spawn_close_pid"
#Pid
ignore = true
[[object.function]]
pattern = "[gs]et_prgname"
#manual pathbuf
ignore = true
[[object.function]]
name = "get_user_name"
[object.function.return]
string_type = "os_string"
[[object.function]]
name = "get_real_name"
[object.function.return]
string_type = "os_string"
[[object.function]]
pattern = "setenv"
[[object.function.parameter]]
name = "variable"
string_type = "os_string"
[[object.function.parameter]]
name = "value"
string_type = "os_string"
[object.function.return]
bool_return_is_error = "Failed to set environment variable"
[[object.function]]
pattern = "getenv"
[[object.function.parameter]]
name = "variable"
string_type = "os_string"
[object.function.return]
string_type = "os_string"
[[object.function]]
name = "unsetenv"
[[object.function.parameter]]
name = "variable"
string_type = "os_string"
[[object.function]]
name = "file_get_contents"
# Use glib::Slice<u8> as return value
manual = true
[[object.function]]
name = "mkstemp"
# modifies the string passed in
manual = true
[[object.function]]
name = "mkstemp_full"
# modifies the string passed in
manual = true
[[object.function]]
pattern = "mkdtemp(_full)?"
# needs to be transfer full, see
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2596
manual = true
[[object.function]]
name = "strdup"
#not needed
ignore = true
[[object.function]]
pattern = "unix_.+"
cfg_condition = "unix"
[[object.function]]
name = "spawn_command_line_async"
cfg_condition = "unix"
[[object.function.parameter]]
name = "command_line"
string_type = "os_string"
[[object.function]]
name = "unix_open_pipe"
manual = true
[[object.function]]
name = "convert_with_fallback"
manual = true
[[object.function]]
name = "unicode_canonical_decomposition"
#out param not in .gir
ignore = true
[[object.function]]
name = "unicode_canonical_ordering"
#UCS-4 encoded string
ignore = true
[[object.function]]
name = "convert"
manual = true
[[object.function]]
name = "filename_from_utf8"
manual = true
[[object.function]]
name = "filename_to_utf8"
manual = true
[[object.function]]
name = "locale_from_utf8"
manual = true
[[object.function]]
name = "locale_to_utf8"
manual = true
[[object.function]]
name = "child_watch_source_new"
# Need manual bindings to be useful
ignore = true
[[object.function]]
name = "idle_source_new"
# Need manual bindings to be useful
ignore = true
[[object.function]]
name = "timeout_source_new"
# Need manual bindings to be useful
ignore = true
[[object.function]]
name = "timeout_source_new_seconds"
# Need manual bindings to be useful
ignore = true
[[object.function]]
name = "unix_signal_source_new"
# Need manual bindings to be useful
ignore = true
[[object.function]]
pattern = "unix_fd.*"
# Need manual binding for RawFd
ignore = true
[[object.function]]
name = "close"
# Need manual binding for RawFd
ignore = true
[[object.function]]
name = "log_writer_is_journald"
# Need manual binding for RawFd
ignore = true
[[object.function]]
name = "log_writer_supports_color"
# Need manual binding for RawFd
ignore = true
[[object.function]]
name = "unix_set_fd_nonblocking"
# Need manual binding for RawFd
ignore = true
[[object.function]]
name = "environ_getenv"
# manual input &[OsString]
ignore = true
[[object.function]]
pattern = "environ_(un)?setenv"
# unusable
ignore = true
[[object.function]]
name = "get_charset"
# boolean return value
ignore = true
[[object.function]]
name = "get_environ"
[object.function.return]
string_type = "os_string"
[[object.function]]
name = "listenv"
[object.function.return]
string_type = "os_string"
[[object.function]]
name = "shell_parse_argv"
[[object.function.parameter]]
name = "command_line"
string_type = "os_string"
[[object.function.parameter]]
name = "argvp"
string_type = "os_string"
[[object.function]]
name = "shell_quote"
[[object.function.parameter]]
name = "unquoted_string"
string_type = "os_string"
[object.function.return]
string_type = "os_string"
[[object.function]]
name = "shell_unquote"
[[object.function.parameter]]
name = "quoted_string"
string_type = "os_string"
[object.function.return]
string_type = "os_string"
[[object.function]]
name = "intern_static_string"
ignore = true
[[object.function]]
name = "intern_string"
ignore = true
[[object.function]]
# Pid conversion issue
name = "child_watch_add"
ignore = true
[[object.function]]
# Pid conversion issue
name = "child_watch_add_full"
ignore = true
# idle_* and timeout_* are manually implemented
[[object.function]]
name = "idle_add"
ignore = true
[[object.function]]
name = "idle_add_full"
ignore = true
[[object.function]]
name = "idle_add_once"
ignore = true
[[object.function]]
name = "timeout_add"
ignore = true
[[object.function]]
name = "timeout_add_full"
ignore = true
[[object.function]]
name = "timeout_add_once"
ignore = true
[[object.function]]
name = "timeout_add_seconds"
ignore = true
[[object.function]]
name = "timeout_add_seconds_full"
ignore = true
[[object.function]]
name = "timeout_add_seconds_once"
ignore = true # we have a manual impl already
[[object.function]]
name = "unix_signal_add"
ignore = true
[[object.function]]
name = "unix_signal_add_full"
ignore = true
[[object.function]]
pattern = "rc_box_.+"
#need manual implementation
ignore = true
[[object.function]]
pattern = "ref_count_.+"
#need manual implementation
ignore = true
[[object.function]]
pattern = "ref_string_.+"
#need manual implementation
ignore = true
[[object.function]]
name = "spawn_async_with_fds"
# generates invalid FDs
ignore = true
[[object.function]]
name = "spawn_async_with_pipes"
# generates invalid FDs
ignore = true
[[object.function]]
name = "log_set_handler"
# leaks closure
ignore = true
[[object.function]]
name = "log_default_handler"
# last argument is unused
ignore = true
[[object.function]]
name = "log_set_handler_full"
# from_glib_borrow on non-pointer objects
ignore = true
[[object.function]]
name = "log_set_writer_func"
# from_glib_borrow on non-pointer objects
ignore = true
[[object.function]]
name = "log_structured"
# implemented with a macro because of var args and weird arguments pairing
ignore = true
[[object.function]]
name = "log"
# implemented with a macro because of var args
ignore = true
[[object.function]]
name = "log_set_default_handler"
# It returned the old callback, which is problematic
ignore = true
[[object.function]]
name = "log_remove_handler"
# Change handler_id parameter type to LogHandlerId
ignore = true
[[object.function]]
name = "log_set_always_fatal"
# update of arguments and return type
ignore = true
[[object.function]]
name = "log_variant"
# usage of LogLevelFlags
ignore = true
[[object.function]]
name = "log_set_fatal_mask"
# update of arguments and return type
ignore = true
[[object.constant]]
pattern = "DIR_SEPARATOR(_S)?"
#not cross-platform
ignore = true
[[object.constant]]
pattern = "SEARCHPATH_SEPARATOR(_S)?"
#not cross-platform
ignore = true
[[object.constant]]
name = "MODULE_SUFFIX"
#not cross-platform
ignore = true
[[object.constant]]
pattern = "GU?INT(16)?(32)?(64)?(PTR)?_FORMAT"
#for C printf
ignore = true
[[object.constant]]
pattern = "GU?INT(16)?(32)?(64)?(PTR)?_MODIFIER"
#for C printf
ignore = true
[[object.constant]]
pattern = "GS?SIZE_(FORMAT)?(MODIFIER)?"
#for C printf
ignore = true
[[object.constant]]
pattern = "GNUC_(PRETTY_)?FUNCTION"
#for C printf
ignore = true
[[object.constant]]
pattern = "PID_FORMAT"
#for C printf
ignore = true
[[object.constant]]
pattern = "POLLFD_FORMAT"
#for C printf
ignore = true
[[object.function]]
name = "variant_get_gtype"
# get_type() function that should be used in StaticType impl instead
ignore = true
[[object.function]]
pattern = "unichar_((break_)?type|get_(script|mirror_char)|combining_class|is(mark|graph|punct|title|defined|wide(_cjk)?|zerowidth)|totitle|(fully_)?decompose|compose)"
manual = true # defined as extension methods on char
[[object]]
name = "GLib.Checksum"
status = "generate"
concurrency = "send+sync"
[[object.function]]
name = "get_digest"
#wrong array definition
manual = true
[[object.function]]
name = "get_string"
#consume
manual = true
[[object]]
name = "GLib.ChecksumType"
status = "generate"
[[object.member]]
name = "sha384"
version = "2.52"
[[object]]
name = "GLib.DateTime"
status = "generate"
concurrency = "send+sync"
[[object.function]]
name = "new_local"
rename = "from_local"
[[object.function]]
name = "new_now"
rename = "now"
[[object.function]]
name = "new_now_local"
rename = "now_local"
[[object.function]]
name = "new_now_utc"
rename = "now_utc"
[[object.function]]
name = "new_utc"
rename = "from_utc"
[[object.function]]
pattern = ".+"
[object.function.return]
nullable_return_is_error = "Invalid date"
[[object]]
name = "GLib.FileTest" # used for tests
status = "generate"
visibility = "crate"
[[object]]
name = "GLib.FormatSizeFlags"
status = "generate"
[[object.member]]
name = "only_value"
version = "2.74"
[[object.member]]
name = "only_unit"
version = "2.74"
[[object]]
name = "GLib.IConv"
status = "manual"
concurrency = "send"
[[object.function]]
name = "open"
rename = "new"
constructor = true
[[object]]
name = "GLib.KeyFile"
status = "generate"
[[object.function]]
name = "load_from_data_dirs"
manual = true
[[object.function]]
name = "load_from_dirs"
manual = true
[[object.function]]
name = "save_to_file"
# file parameter needs to be a PathBuf
manual = true
[[object.function]]
name = "set_locale_string_list"
#[&str] to *mut i8
ignore = true
[[object.function]]
name = "set_string_list"
#[&str] to *mut i8
ignore = true
[[object.function]]
name = "to_data"
manual = true
[[object.function]]
name = "set_double_list"
#wrong array type
ignore = true
[[object.function]]
name = "set_integer_list"
#wrong array type
ignore = true
[[object.function]]
name = "get_groups"
#return slice of str pointers
manual = true
[[object.function]]
name = "get_keys"
#return slice of str pointers
manual = true
[[object.function]]
name = "get_boolean"
#boolean return value needs to be returned
manual = true
[[object.function]]
name = "has_key"
#boolean return value needs to be returned
manual = true
[[object.function]]
name = "get_boolean_list"
#boolean array needs to be converted to Vec<bool>
manual = true
[[object.function]]
name = "get_string"
# can return an error but still a value to be freed
manual = true
[[object.function]]
name = "get_string_list"
# can return an error but still a value to be freed
manual = true
[[object.function]]
name = "get_locale_string"
# can return an error but still a value to be freed
manual = true
[[object.function]]
name = "get_locale_string_list"
# can return an error but still a value to be freed
manual = true
[[object.function]]
name = "get_comment"
[[object.function.parameter]]
name = "key"
nullable = true
[[object]]
name = "GLib.MainContext"
status = "generate"
concurrency = "send+sync"
[[object.function]]
name = "prepare"
# out parameter not marked as such
manual = true
[[object.function]]
name = "find_source_by_id"
# source id is a newtype
manual = true
[[object.function]]
name = "invoke"
manual = true
[[object.function]]
name = "invoke_full"
manual = true
[[object.function]]
name = "acquire"
# implemented with a guard type
manual = true
[[object.function]]
name = "release"
# implemented with a guard type
manual = true
[[object.function]]
name = "push_thread_default"
# implemented with a guard type
manual = true
[[object.function]]
name = "pop_thread_default"
# implemented with a guard type
manual = true
[[object]]
name = "GLib.MainLoop"
status = "generate"
concurrency = "send+sync"
[[object]]
name = "GLib.MarkupParseContext"
status = "generate"
[[object.function]]
name = "get_user_data"
ignore = true # unsafe pointer
[[object]]
name = "GLib.Source"
status = "generate"
concurrency = "send+sync"
[[object.function]]
pattern = "set_.+"
# Setters are generally not thread-safe
# while GSource itself is
ignore = true
[[object.function]]
pattern = "attach"
# return type
ignore = true
[[object.function]]
pattern = "get_id"
# unsafe as it would allow multiple times to remove
ignore = true
[[object.function]]
pattern = "remove"
# parameter type
ignore = true
[[object]]
name = "GLib.ThreadPool"
status = "manual"
concurrency = "send+sync"
[[object.function]]
name = "new_shared"
rename = "shared"
[[object.function]]
name = "new_exclusive"
rename = "exclusive"
[[object]]
name = "GLib.TimeSpan"
status = "manual"
conversion_type = "scalar"
[[object]]
name = "GLib.TimeZone"
status = "generate"
concurrency = "send+sync"
[[object.function]]
name = "new_identifier"
rename = "from_identifier"
[[object.function]]
name = "new_local"
rename = "local"
[[object.function]]
name = "new_offset"
rename = "from_offset"
[[object.function]]
name = "new_utc"
rename = "utc"
[[object.function]]
name = "adjust_time"
# in-out parameter
manual = true
[[object]]
name = "GLib.UnicodeBreakType"
status = "generate"
[[object.member]]
name = "close_paranthesis"
ignore = true
[[object]]
name = "GLib.UnicodeScript"
status = "generate"
[[object.member]]
name = "cypro_minoan"
version = "2.72"
[[object.member]]
name = "old_uyghur"
version = "2.72"
[[object.member]]
name = "tangsa"
version = "2.72"
[[object.member]]
name = "toto"
version = "2.72"
[[object.member]]
name = "vithkuqi"
version = "2.72"
[[object.member]]
name = "math"
version = "2.72"
[[object.member]]
name = "kawi"
version = "2.74"
[[object.member]]
name = "nag_mundari"
version = "2.74"
[[object]]
name = "GLib.Uri"
status = "generate"
concurrency = "send+sync"
+149
View File
@@ -0,0 +1,149 @@
[options]
girs_dir = "gir-files"
library = "GObject"
version = "2.0"
min_cfg_version = "2.56"
target_path = "."
auto_path = "src/gobject/auto"
work_mode = "normal"
single_version_file = true
deprecate_by_min_version = true
trust_return_value_nullability = true
girs_directories = ["../gir-files"]
generate = [
"GObject.BindingFlags",
"GObject.SignalFlags",
]
ignore = [
]
manual = [
"GLib.Quark",
"GObject.Object",
"GObject.Value",
"GObject.ParamFlags",
"GObject.ParamSpec",
"GObject.ParamSpecChar",
"GObject.ParamSpecUChar",
"GObject.ParamSpecBoolean",
"GObject.ParamSpecInt",
"GObject.ParamSpecUInt",
"GObject.ParamSpecLong",
"GObject.ParamSpecULong",
"GObject.ParamSpecInt64",
"GObject.ParamSpecUInt64",
"GObject.ParamSpecUnichar",
"GObject.ParamSpecEnum",
"GObject.ParamSpecFlags",
"GObject.ParamSpecFloat",
"GObject.ParamSpecDouble",
"GObject.ParamSpecString",
"GObject.ParamSpecParam",
"GObject.ParamSpecBoxed",
"GObject.ParamSpecPointer",
"GObject.ParamSpecValueArray",
"GObject.ParamSpecObject",
"GObject.ParamSpecOverride",
"GObject.ParamSpecGType",
"GObject.ParamSpecVariant",
]
[[object]]
name = "GObject.Binding"
status = "generate"
final_type = true
concurrency = "send+sync"
[[object.function]]
name = "get_source"
# Not thread-safe, see https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1745
# Using the GObject properties will be automatically safe with GLib 2.68.
ignore = true
[[object.function]]
name = "get_target"
# Not thread-safe, see https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1745
# Using the GObject properties will be automatically safe with GLib 2.68.
ignore = true
[[object.function]]
name = "dup_source"
# equivalent to manual get_source
ignore = true
[[object.function]]
name = "dup_target"
# equivalent to manual get_target
ignore = true
[[object.property]]
name = "source"
# So we can implement setter/getter with the correct name
ignore = true
[[object.property]]
name = "target"
# So we can implement setter/getter with the correct name
ignore = true
[[object]]
name = "GObject.BindingGroup"
status = "generate"
final_type = true
concurrency = "send+sync"
[[object.function]]
name = "bind"
manual = true
[[object.function]]
name = "bind_full"
ignore = true
[[object.function]]
name = "bind_with_closures"
ignore = true
[[object.function]]
name = "dup_source"
rename = "source"
[[object.property]]
name = "source"
generate = ["notify"]
[[object]]
name = "GObject.SignalGroup"
status = "generate"
final_type = true
concurrency = "send+sync"
version = "2.74"
[[object.function]]
name = "new"
rename = "with_type"
[[object.function]]
name = "connect"
manual = true
[[object.function]]
name = "connect_after"
ignore = true
[[object.function]]
name = "connect_data"
ignore = true
[[object.function]]
name = "connect_object"
ignore = true
[[object.function]]
name = "connect_swapped"
ignore = true
[[object.function]]
name = "connect_closure"
manual = true
[[object.function]]
name = "dup_target"
rename = "target"
[[object.property]]
name = "target"
# notify signal is racy, use connect_bind/connect_unbind instead
generate = []
[[object.signal]]
name = "bind"
manual = true
[[object.signal]]
name = "unbind"
manual = true
[[object]]
name = "GObject.*"
status = "ignore"
+18
View File
@@ -0,0 +1,18 @@
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+101
View File
@@ -0,0 +1,101 @@
# Rust GLib and GObject bindings
__Rust__ bindings and wrappers for [GLib](https://docs.gtk.org/glib/), part of [gtk-rs-core](https://github.com/gtk-rs/gtk-rs-core).
GLib __2.56__ is the lowest supported version for the underlying library.
This library contains bindings to GLib and GObject types and APIs as well as
common building blocks used in both handmade and machine generated
bindings to GTK and other GLib-based libraries.
It is the foundation for higher level libraries with uniform Rusty (safe and
strongly typed) APIs. It avoids exposing GLib-specific data types where
possible and is not meant to provide comprehensive GLib bindings, which
would often amount to duplicating the Rust Standard Library or other utility
crates.
## Minimum supported Rust version
Currently, the minimum supported Rust version is `1.70.0`.
## Dynamic typing
Most types in the GLib family have [`Type`] identifiers.
Their corresponding Rust types implement the [`StaticType`] trait.
A dynamically typed [`Value`] can carry values of any [`StaticType`].
[`Variant`](struct@Variant)s can carry values of [`StaticVariantType`].
## Errors
Errors are represented by [`Error`], which can
carry values from various [error domains](error::ErrorDomain) such as
[`FileError`].
## Objects
Each class and interface has a corresponding smart pointer struct
representing an instance of that type (e.g. [`Object`] for `GObject` or
`gtk::Widget` for `GtkWidget`). They are reference counted and feature
interior mutability similarly to Rust's `Rc<RefCell<T>>` idiom.
Consequently, cloning objects is cheap and their methods never require
mutable borrows. Two smart pointers are equal if they point to the same
object.
The root of the object hierarchy is [`Object`].
Inheritance and subtyping is denoted with the [`IsA`]
marker trait. The [`Cast`] trait enables upcasting
and downcasting.
Interfaces and non-leaf classes also have corresponding traits (e.g.
[`ObjectExt`] or `gtk::WidgetExt`), which are blanketly implemented for all
their subtypes.
You can create new subclasses of [`Object`] or other object types. Look at
the module's documentation for further details and a code example.
## Under the hood
GLib-based libraries largely operate on pointers to various boxed or
reference counted structures so the bindings have to implement corresponding
smart pointers (wrappers), which encapsulate resource management and safety
checks. Such wrappers are defined via the
[`wrapper`][`macro@wrapper`] macro, which uses abstractions
defined in the [`wrapper`][`mod@wrapper`], [`boxed`][`mod@boxed`],
[`shared`][`mod@shared`] and [`object`][`mod@object`] modules.
The [`translate`][`mod@translate`] module defines and partly implements
conversions between high level Rust types (including the aforementioned
wrappers) and their FFI counterparts.
## Documentation
* [Rust API - Stable](https://gtk-rs.org/gtk-rs-core/stable/latest/docs/glib/)
* [Rust API - Development](https://gtk-rs.org/gtk-rs-core/git/docs/glib)
* [C API](https://docs.gtk.org/glib/)
* [GTK Installation instructions](https://www.gtk.org/docs/installations/)
## Using
We recommend using [crates from crates.io](https://crates.io/keywords/gtk-rs),
as [demonstrated here](https://gtk-rs.org/#using).
If you want to track the bleeding edge, use the git dependency instead:
```toml
[dependencies]
glib = { git = "https://github.com/gtk-rs/gtk-rs-core.git", package = "glib" }
```
Avoid mixing versioned and git crates like this:
```toml
# This will not compile
[dependencies]
glib = "0.13"
glib = { git = "https://github.com/gtk-rs/gtk-rs-core.git", package = "glib" }
```
## License
__glib__ is available under the MIT License, please refer to it.
+46
View File
@@ -0,0 +1,46 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use glib::IntoGStr;
pub fn str_into_gstr(c: &mut Criterion) {
c.bench_function("str as IntoGStr", |b| {
b.iter(|| {
"abcdefg".run_with_gstr(|g| {
black_box(g);
})
})
});
}
pub fn string_into_gstr(c: &mut Criterion) {
c.bench_function("String as IntoGStr", |b| {
b.iter(|| {
let mut s = String::from("abcdefg");
s.shrink_to_fit();
assert_eq!(s.capacity(), s.len());
s.run_with_gstr(|g| {
black_box(g);
})
})
});
}
pub fn string_with_capacity_into_gstr(c: &mut Criterion) {
c.bench_function("String::with_capacity as IntoGStr", |b| {
b.iter(|| {
let mut s = String::with_capacity(100);
s.push_str("abcdefg");
assert!(s.capacity() > s.len());
s.run_with_gstr(|g| {
black_box(g);
})
})
});
}
criterion_group!(
benches,
str_into_gstr,
string_into_gstr,
string_with_capacity_into_gstr
);
criterion_main!(benches);
+13
View File
@@ -0,0 +1,13 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
#[allow(unused_imports)]
use crate::auto::*;
#[doc(alias = "GDateDay")]
pub type DateDay = u8;
#[doc(alias = "GDateYear")]
pub type DateYear = u16;
#[doc(alias = "GTime")]
pub type Time = i32;
+46
View File
@@ -0,0 +1,46 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
use crate::{translate::*, ChecksumType};
crate::wrapper! {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Checksum(Boxed<ffi::GChecksum>);
match fn {
copy => |ptr| ffi::g_checksum_copy(ptr),
free => |ptr| ffi::g_checksum_free(ptr),
type_ => || ffi::g_checksum_get_type(),
}
}
impl Checksum {
#[doc(alias = "g_checksum_new")]
pub fn new(checksum_type: ChecksumType) -> Option<Checksum> {
unsafe { from_glib_full(ffi::g_checksum_new(checksum_type.into_glib())) }
}
#[doc(alias = "g_checksum_reset")]
pub fn reset(&mut self) {
unsafe {
ffi::g_checksum_reset(self.to_glib_none_mut().0);
}
}
#[doc(alias = "g_checksum_update")]
pub fn update(&mut self, data: &[u8]) {
let length = data.len() as _;
unsafe {
ffi::g_checksum_update(self.to_glib_none_mut().0, data.to_glib_none().0, length);
}
}
#[doc(alias = "g_checksum_type_get_length")]
pub fn type_get_length(checksum_type: ChecksumType) -> isize {
unsafe { ffi::g_checksum_type_get_length(checksum_type.into_glib()) }
}
}
unsafe impl Send for Checksum {}
unsafe impl Sync for Checksum {}
+105
View File
@@ -0,0 +1,105 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
use crate::GStr;
#[doc(alias = "G_CSET_A_2_Z")]
pub static CSET_A_2_Z: &GStr = unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_CSET_A_2_Z) };
#[doc(alias = "G_CSET_DIGITS")]
pub static CSET_DIGITS: &GStr = unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_CSET_DIGITS) };
#[doc(alias = "G_CSET_a_2_z")]
pub static CSET_a_2_z: &GStr = unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_CSET_a_2_z) };
#[doc(alias = "G_KEY_FILE_DESKTOP_GROUP")]
pub static KEY_FILE_DESKTOP_GROUP: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_GROUP) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_ACTIONS")]
pub static KEY_FILE_DESKTOP_KEY_ACTIONS: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_ACTIONS) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_CATEGORIES")]
pub static KEY_FILE_DESKTOP_KEY_CATEGORIES: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_CATEGORIES) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_COMMENT")]
pub static KEY_FILE_DESKTOP_KEY_COMMENT: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_COMMENT) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE")]
pub static KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_EXEC")]
pub static KEY_FILE_DESKTOP_KEY_EXEC: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_EXEC) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_GENERIC_NAME")]
pub static KEY_FILE_DESKTOP_KEY_GENERIC_NAME: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_GENERIC_NAME) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_HIDDEN")]
pub static KEY_FILE_DESKTOP_KEY_HIDDEN: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_HIDDEN) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_ICON")]
pub static KEY_FILE_DESKTOP_KEY_ICON: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_ICON) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_MIME_TYPE")]
pub static KEY_FILE_DESKTOP_KEY_MIME_TYPE: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_MIME_TYPE) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_NAME")]
pub static KEY_FILE_DESKTOP_KEY_NAME: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_NAME) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN")]
pub static KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY")]
pub static KEY_FILE_DESKTOP_KEY_NO_DISPLAY: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN")]
pub static KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_PATH")]
pub static KEY_FILE_DESKTOP_KEY_PATH: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_PATH) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY")]
pub static KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS")]
pub static KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_TERMINAL")]
pub static KEY_FILE_DESKTOP_KEY_TERMINAL: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_TERMINAL) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_TRY_EXEC")]
pub static KEY_FILE_DESKTOP_KEY_TRY_EXEC: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_TRY_EXEC) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_TYPE")]
pub static KEY_FILE_DESKTOP_KEY_TYPE: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_TYPE) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_URL")]
pub static KEY_FILE_DESKTOP_KEY_URL: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_URL) };
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_VERSION")]
pub static KEY_FILE_DESKTOP_KEY_VERSION: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_VERSION) };
#[doc(alias = "G_KEY_FILE_DESKTOP_TYPE_APPLICATION")]
pub static KEY_FILE_DESKTOP_TYPE_APPLICATION: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_TYPE_APPLICATION) };
#[doc(alias = "G_KEY_FILE_DESKTOP_TYPE_DIRECTORY")]
pub static KEY_FILE_DESKTOP_TYPE_DIRECTORY: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_TYPE_DIRECTORY) };
#[doc(alias = "G_KEY_FILE_DESKTOP_TYPE_LINK")]
pub static KEY_FILE_DESKTOP_TYPE_LINK: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_TYPE_LINK) };
#[doc(alias = "G_OPTION_REMAINING")]
pub static OPTION_REMAINING: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_OPTION_REMAINING) };
#[doc(alias = "G_STR_DELIMITERS")]
pub static STR_DELIMITERS: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_STR_DELIMITERS) };
#[cfg(feature = "v2_60")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_60")))]
#[doc(alias = "G_TEST_OPTION_ISOLATE_DIRS")]
pub static TEST_OPTION_ISOLATE_DIRS: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_TEST_OPTION_ISOLATE_DIRS) };
#[doc(alias = "G_URI_RESERVED_CHARS_GENERIC_DELIMITERS")]
pub static URI_RESERVED_CHARS_GENERIC_DELIMITERS: &GStr =
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_URI_RESERVED_CHARS_GENERIC_DELIMITERS) };
#[doc(alias = "G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS")]
pub static URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS: &GStr = unsafe {
GStr::from_utf8_with_nul_unchecked(ffi::G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS)
};
+502
View File
@@ -0,0 +1,502 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
use crate::{translate::*, BoolError, TimeSpan, TimeZone};
use std::{cmp, hash, mem};
crate::wrapper! {
#[derive(Debug)]
pub struct DateTime(Shared<ffi::GDateTime>);
match fn {
ref => |ptr| ffi::g_date_time_ref(ptr),
unref => |ptr| ffi::g_date_time_unref(ptr),
type_ => || ffi::g_date_time_get_type(),
}
}
impl DateTime {
#[doc(alias = "g_date_time_new")]
pub fn new(
tz: &TimeZone,
year: i32,
month: i32,
day: i32,
hour: i32,
minute: i32,
seconds: f64,
) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_new(
tz.to_glib_none().0,
year,
month,
day,
hour,
minute,
seconds,
))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_new_from_iso8601")]
#[doc(alias = "new_from_iso8601")]
pub fn from_iso8601(text: &str, default_tz: Option<&TimeZone>) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_new_from_iso8601(
text.to_glib_none().0,
default_tz.to_glib_none().0,
))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
//#[cfg_attr(feature = "v2_62", deprecated = "Since 2.62")]
//#[allow(deprecated)]
//#[doc(alias = "g_date_time_new_from_timeval_local")]
//#[doc(alias = "new_from_timeval_local")]
//pub fn from_timeval_local(tv: /*Ignored*/&TimeVal) -> Result<DateTime, BoolError> {
// unsafe { TODO: call ffi:g_date_time_new_from_timeval_local() }
//}
//#[cfg_attr(feature = "v2_62", deprecated = "Since 2.62")]
//#[allow(deprecated)]
//#[doc(alias = "g_date_time_new_from_timeval_utc")]
//#[doc(alias = "new_from_timeval_utc")]
//pub fn from_timeval_utc(tv: /*Ignored*/&TimeVal) -> Result<DateTime, BoolError> {
// unsafe { TODO: call ffi:g_date_time_new_from_timeval_utc() }
//}
#[doc(alias = "g_date_time_new_from_unix_local")]
#[doc(alias = "new_from_unix_local")]
pub fn from_unix_local(t: i64) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_new_from_unix_local(t))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_new_from_unix_utc")]
#[doc(alias = "new_from_unix_utc")]
pub fn from_unix_utc(t: i64) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_new_from_unix_utc(t))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_new_local")]
#[doc(alias = "new_local")]
pub fn from_local(
year: i32,
month: i32,
day: i32,
hour: i32,
minute: i32,
seconds: f64,
) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_new_local(
year, month, day, hour, minute, seconds,
))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_new_now")]
#[doc(alias = "new_now")]
pub fn now(tz: &TimeZone) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_new_now(tz.to_glib_none().0))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_new_now_local")]
#[doc(alias = "new_now_local")]
pub fn now_local() -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_new_now_local())
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_new_now_utc")]
#[doc(alias = "new_now_utc")]
pub fn now_utc() -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_new_now_utc())
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_new_utc")]
#[doc(alias = "new_utc")]
pub fn from_utc(
year: i32,
month: i32,
day: i32,
hour: i32,
minute: i32,
seconds: f64,
) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_new_utc(
year, month, day, hour, minute, seconds,
))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_add")]
pub fn add(&self, timespan: TimeSpan) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_add(
self.to_glib_none().0,
timespan.into_glib(),
))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_add_days")]
pub fn add_days(&self, days: i32) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_add_days(self.to_glib_none().0, days))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_add_full")]
pub fn add_full(
&self,
years: i32,
months: i32,
days: i32,
hours: i32,
minutes: i32,
seconds: f64,
) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_add_full(
self.to_glib_none().0,
years,
months,
days,
hours,
minutes,
seconds,
))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_add_hours")]
pub fn add_hours(&self, hours: i32) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_add_hours(self.to_glib_none().0, hours))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_add_minutes")]
pub fn add_minutes(&self, minutes: i32) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_add_minutes(
self.to_glib_none().0,
minutes,
))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_add_months")]
pub fn add_months(&self, months: i32) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_add_months(self.to_glib_none().0, months))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_add_seconds")]
pub fn add_seconds(&self, seconds: f64) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_add_seconds(
self.to_glib_none().0,
seconds,
))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_add_weeks")]
pub fn add_weeks(&self, weeks: i32) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_add_weeks(self.to_glib_none().0, weeks))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_add_years")]
pub fn add_years(&self, years: i32) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_add_years(self.to_glib_none().0, years))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_compare")]
fn compare(&self, dt2: &DateTime) -> i32 {
unsafe {
ffi::g_date_time_compare(
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(self).0 as ffi::gconstpointer,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(dt2).0 as ffi::gconstpointer,
)
}
}
#[doc(alias = "g_date_time_difference")]
pub fn difference(&self, begin: &DateTime) -> TimeSpan {
unsafe {
from_glib(ffi::g_date_time_difference(
self.to_glib_none().0,
begin.to_glib_none().0,
))
}
}
#[doc(alias = "g_date_time_equal")]
fn equal(&self, dt2: &DateTime) -> bool {
unsafe {
from_glib(ffi::g_date_time_equal(
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(self).0 as ffi::gconstpointer,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(dt2).0 as ffi::gconstpointer,
))
}
}
#[doc(alias = "g_date_time_format")]
pub fn format(&self, format: &str) -> Result<crate::GString, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_format(
self.to_glib_none().0,
format.to_glib_none().0,
))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[cfg(feature = "v2_62")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_62")))]
#[doc(alias = "g_date_time_format_iso8601")]
pub fn format_iso8601(&self) -> Result<crate::GString, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_format_iso8601(self.to_glib_none().0))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_get_day_of_month")]
#[doc(alias = "get_day_of_month")]
pub fn day_of_month(&self) -> i32 {
unsafe { ffi::g_date_time_get_day_of_month(self.to_glib_none().0) }
}
#[doc(alias = "g_date_time_get_day_of_week")]
#[doc(alias = "get_day_of_week")]
pub fn day_of_week(&self) -> i32 {
unsafe { ffi::g_date_time_get_day_of_week(self.to_glib_none().0) }
}
#[doc(alias = "g_date_time_get_day_of_year")]
#[doc(alias = "get_day_of_year")]
pub fn day_of_year(&self) -> i32 {
unsafe { ffi::g_date_time_get_day_of_year(self.to_glib_none().0) }
}
#[doc(alias = "g_date_time_get_hour")]
#[doc(alias = "get_hour")]
pub fn hour(&self) -> i32 {
unsafe { ffi::g_date_time_get_hour(self.to_glib_none().0) }
}
#[doc(alias = "g_date_time_get_microsecond")]
#[doc(alias = "get_microsecond")]
pub fn microsecond(&self) -> i32 {
unsafe { ffi::g_date_time_get_microsecond(self.to_glib_none().0) }
}
#[doc(alias = "g_date_time_get_minute")]
#[doc(alias = "get_minute")]
pub fn minute(&self) -> i32 {
unsafe { ffi::g_date_time_get_minute(self.to_glib_none().0) }
}
#[doc(alias = "g_date_time_get_month")]
#[doc(alias = "get_month")]
pub fn month(&self) -> i32 {
unsafe { ffi::g_date_time_get_month(self.to_glib_none().0) }
}
#[doc(alias = "g_date_time_get_second")]
#[doc(alias = "get_second")]
pub fn second(&self) -> i32 {
unsafe { ffi::g_date_time_get_second(self.to_glib_none().0) }
}
#[doc(alias = "g_date_time_get_seconds")]
#[doc(alias = "get_seconds")]
pub fn seconds(&self) -> f64 {
unsafe { ffi::g_date_time_get_seconds(self.to_glib_none().0) }
}
#[cfg(feature = "v2_58")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_58")))]
#[doc(alias = "g_date_time_get_timezone")]
#[doc(alias = "get_timezone")]
pub fn timezone(&self) -> TimeZone {
unsafe { from_glib_none(ffi::g_date_time_get_timezone(self.to_glib_none().0)) }
}
#[doc(alias = "g_date_time_get_timezone_abbreviation")]
#[doc(alias = "get_timezone_abbreviation")]
pub fn timezone_abbreviation(&self) -> crate::GString {
unsafe {
from_glib_none(ffi::g_date_time_get_timezone_abbreviation(
self.to_glib_none().0,
))
}
}
#[doc(alias = "g_date_time_get_utc_offset")]
#[doc(alias = "get_utc_offset")]
pub fn utc_offset(&self) -> TimeSpan {
unsafe { from_glib(ffi::g_date_time_get_utc_offset(self.to_glib_none().0)) }
}
#[doc(alias = "g_date_time_get_week_numbering_year")]
#[doc(alias = "get_week_numbering_year")]
pub fn week_numbering_year(&self) -> i32 {
unsafe { ffi::g_date_time_get_week_numbering_year(self.to_glib_none().0) }
}
#[doc(alias = "g_date_time_get_week_of_year")]
#[doc(alias = "get_week_of_year")]
pub fn week_of_year(&self) -> i32 {
unsafe { ffi::g_date_time_get_week_of_year(self.to_glib_none().0) }
}
#[doc(alias = "g_date_time_get_year")]
#[doc(alias = "get_year")]
pub fn year(&self) -> i32 {
unsafe { ffi::g_date_time_get_year(self.to_glib_none().0) }
}
#[doc(alias = "g_date_time_get_ymd")]
#[doc(alias = "get_ymd")]
pub fn ymd(&self) -> (i32, i32, i32) {
unsafe {
let mut year = mem::MaybeUninit::uninit();
let mut month = mem::MaybeUninit::uninit();
let mut day = mem::MaybeUninit::uninit();
ffi::g_date_time_get_ymd(
self.to_glib_none().0,
year.as_mut_ptr(),
month.as_mut_ptr(),
day.as_mut_ptr(),
);
(year.assume_init(), month.assume_init(), day.assume_init())
}
}
#[doc(alias = "g_date_time_hash")]
fn hash(&self) -> u32 {
unsafe {
ffi::g_date_time_hash(
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(self).0 as ffi::gconstpointer,
)
}
}
#[doc(alias = "g_date_time_is_daylight_savings")]
pub fn is_daylight_savings(&self) -> bool {
unsafe { from_glib(ffi::g_date_time_is_daylight_savings(self.to_glib_none().0)) }
}
#[doc(alias = "g_date_time_to_local")]
pub fn to_local(&self) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_to_local(self.to_glib_none().0))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
//#[cfg_attr(feature = "v2_62", deprecated = "Since 2.62")]
//#[allow(deprecated)]
//#[doc(alias = "g_date_time_to_timeval")]
//pub fn to_timeval(&self, tv: /*Ignored*/&mut TimeVal) -> bool {
// unsafe { TODO: call ffi:g_date_time_to_timeval() }
//}
#[doc(alias = "g_date_time_to_timezone")]
pub fn to_timezone(&self, tz: &TimeZone) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_to_timezone(
self.to_glib_none().0,
tz.to_glib_none().0,
))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
#[doc(alias = "g_date_time_to_unix")]
pub fn to_unix(&self) -> i64 {
unsafe { ffi::g_date_time_to_unix(self.to_glib_none().0) }
}
#[doc(alias = "g_date_time_to_utc")]
pub fn to_utc(&self) -> Result<DateTime, BoolError> {
unsafe {
Option::<_>::from_glib_full(ffi::g_date_time_to_utc(self.to_glib_none().0))
.ok_or_else(|| crate::bool_error!("Invalid date"))
}
}
}
impl PartialOrd for DateTime {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for DateTime {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.compare(other).cmp(&0)
}
}
impl PartialEq for DateTime {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.equal(other)
}
}
impl Eq for DateTime {}
impl hash::Hash for DateTime {
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher,
{
hash::Hash::hash(&self.hash(), state)
}
}
unsafe impl Send for DateTime {}
unsafe impl Sync for DateTime {}
File diff suppressed because it is too large. Load diff
+636
View File
@@ -0,0 +1,636 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
use crate::{bitflags::bitflags, prelude::*, translate::*};
use std::fmt;
#[cfg(feature = "v2_66")]
bitflags! {
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[doc(alias = "GFileSetContentsFlags")]
pub struct FileSetContentsFlags: u32 {
#[doc(alias = "G_FILE_SET_CONTENTS_NONE")]
const NONE = ffi::G_FILE_SET_CONTENTS_NONE as _;
#[doc(alias = "G_FILE_SET_CONTENTS_CONSISTENT")]
const CONSISTENT = ffi::G_FILE_SET_CONTENTS_CONSISTENT as _;
#[doc(alias = "G_FILE_SET_CONTENTS_DURABLE")]
const DURABLE = ffi::G_FILE_SET_CONTENTS_DURABLE as _;
#[doc(alias = "G_FILE_SET_CONTENTS_ONLY_EXISTING")]
const ONLY_EXISTING = ffi::G_FILE_SET_CONTENTS_ONLY_EXISTING as _;
}
}
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
impl fmt::Display for FileSetContentsFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
#[doc(hidden)]
impl IntoGlib for FileSetContentsFlags {
type GlibType = ffi::GFileSetContentsFlags;
#[inline]
fn into_glib(self) -> ffi::GFileSetContentsFlags {
self.bits()
}
}
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
#[doc(hidden)]
impl FromGlib<ffi::GFileSetContentsFlags> for FileSetContentsFlags {
#[inline]
unsafe fn from_glib(value: ffi::GFileSetContentsFlags) -> Self {
Self::from_bits_truncate(value)
}
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[doc(alias = "GFileTest")]
pub(crate) struct FileTest: u32 {
#[doc(alias = "G_FILE_TEST_IS_REGULAR")]
const IS_REGULAR = ffi::G_FILE_TEST_IS_REGULAR as _;
#[doc(alias = "G_FILE_TEST_IS_SYMLINK")]
const IS_SYMLINK = ffi::G_FILE_TEST_IS_SYMLINK as _;
#[doc(alias = "G_FILE_TEST_IS_DIR")]
const IS_DIR = ffi::G_FILE_TEST_IS_DIR as _;
#[doc(alias = "G_FILE_TEST_IS_EXECUTABLE")]
const IS_EXECUTABLE = ffi::G_FILE_TEST_IS_EXECUTABLE as _;
#[doc(alias = "G_FILE_TEST_EXISTS")]
const EXISTS = ffi::G_FILE_TEST_EXISTS as _;
}
}
impl fmt::Display for FileTest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
#[doc(hidden)]
impl IntoGlib for FileTest {
type GlibType = ffi::GFileTest;
#[inline]
fn into_glib(self) -> ffi::GFileTest {
self.bits()
}
}
#[doc(hidden)]
impl FromGlib<ffi::GFileTest> for FileTest {
#[inline]
unsafe fn from_glib(value: ffi::GFileTest) -> Self {
Self::from_bits_truncate(value)
}
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[doc(alias = "GFormatSizeFlags")]
pub struct FormatSizeFlags: u32 {
#[doc(alias = "G_FORMAT_SIZE_DEFAULT")]
const DEFAULT = ffi::G_FORMAT_SIZE_DEFAULT as _;
#[doc(alias = "G_FORMAT_SIZE_LONG_FORMAT")]
const LONG_FORMAT = ffi::G_FORMAT_SIZE_LONG_FORMAT as _;
#[doc(alias = "G_FORMAT_SIZE_IEC_UNITS")]
const IEC_UNITS = ffi::G_FORMAT_SIZE_IEC_UNITS as _;
#[doc(alias = "G_FORMAT_SIZE_BITS")]
const BITS = ffi::G_FORMAT_SIZE_BITS as _;
#[cfg(feature = "v2_74")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
#[doc(alias = "G_FORMAT_SIZE_ONLY_VALUE")]
const ONLY_VALUE = ffi::G_FORMAT_SIZE_ONLY_VALUE as _;
#[cfg(feature = "v2_74")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
#[doc(alias = "G_FORMAT_SIZE_ONLY_UNIT")]
const ONLY_UNIT = ffi::G_FORMAT_SIZE_ONLY_UNIT as _;
}
}
impl fmt::Display for FormatSizeFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
#[doc(hidden)]
impl IntoGlib for FormatSizeFlags {
type GlibType = ffi::GFormatSizeFlags;
#[inline]
fn into_glib(self) -> ffi::GFormatSizeFlags {
self.bits()
}
}
#[doc(hidden)]
impl FromGlib<ffi::GFormatSizeFlags> for FormatSizeFlags {
#[inline]
unsafe fn from_glib(value: ffi::GFormatSizeFlags) -> Self {
Self::from_bits_truncate(value)
}
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[doc(alias = "GIOCondition")]
pub struct IOCondition: u32 {
#[doc(alias = "G_IO_IN")]
const IN = ffi::G_IO_IN as _;
#[doc(alias = "G_IO_OUT")]
const OUT = ffi::G_IO_OUT as _;
#[doc(alias = "G_IO_PRI")]
const PRI = ffi::G_IO_PRI as _;
#[doc(alias = "G_IO_ERR")]
const ERR = ffi::G_IO_ERR as _;
#[doc(alias = "G_IO_HUP")]
const HUP = ffi::G_IO_HUP as _;
#[doc(alias = "G_IO_NVAL")]
const NVAL = ffi::G_IO_NVAL as _;
}
}
impl fmt::Display for IOCondition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
#[doc(hidden)]
impl IntoGlib for IOCondition {
type GlibType = ffi::GIOCondition;
#[inline]
fn into_glib(self) -> ffi::GIOCondition {
self.bits()
}
}
#[doc(hidden)]
impl FromGlib<ffi::GIOCondition> for IOCondition {
#[inline]
unsafe fn from_glib(value: ffi::GIOCondition) -> Self {
Self::from_bits_truncate(value)
}
}
impl StaticType for IOCondition {
#[inline]
#[doc(alias = "g_io_condition_get_type")]
fn static_type() -> crate::Type {
unsafe { from_glib(ffi::g_io_condition_get_type()) }
}
}
impl crate::HasParamSpec for IOCondition {
type ParamSpec = crate::ParamSpecFlags;
type SetValue = Self;
type BuilderFn = fn(&str) -> crate::ParamSpecFlagsBuilder<Self>;
fn param_spec_builder() -> Self::BuilderFn {
Self::ParamSpec::builder
}
}
impl crate::value::ValueType for IOCondition {
type Type = Self;
}
unsafe impl<'a> crate::value::FromValue<'a> for IOCondition {
type Checker = crate::value::GenericValueTypeChecker<Self>;
#[inline]
unsafe fn from_value(value: &'a crate::Value) -> Self {
from_glib(crate::gobject_ffi::g_value_get_flags(
value.to_glib_none().0,
))
}
}
impl ToValue for IOCondition {
#[inline]
fn to_value(&self) -> crate::Value {
let mut value = crate::Value::for_value_type::<Self>();
unsafe {
crate::gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, self.into_glib());
}
value
}
#[inline]
fn value_type(&self) -> crate::Type {
Self::static_type()
}
}
impl From<IOCondition> for crate::Value {
#[inline]
fn from(v: IOCondition) -> Self {
ToValue::to_value(&v)
}
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[doc(alias = "GKeyFileFlags")]
pub struct KeyFileFlags: u32 {
#[doc(alias = "G_KEY_FILE_NONE")]
const NONE = ffi::G_KEY_FILE_NONE as _;
#[doc(alias = "G_KEY_FILE_KEEP_COMMENTS")]
const KEEP_COMMENTS = ffi::G_KEY_FILE_KEEP_COMMENTS as _;
#[doc(alias = "G_KEY_FILE_KEEP_TRANSLATIONS")]
const KEEP_TRANSLATIONS = ffi::G_KEY_FILE_KEEP_TRANSLATIONS as _;
}
}
impl fmt::Display for KeyFileFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
#[doc(hidden)]
impl IntoGlib for KeyFileFlags {
type GlibType = ffi::GKeyFileFlags;
#[inline]
fn into_glib(self) -> ffi::GKeyFileFlags {
self.bits()
}
}
#[doc(hidden)]
impl FromGlib<ffi::GKeyFileFlags> for KeyFileFlags {
#[inline]
unsafe fn from_glib(value: ffi::GKeyFileFlags) -> Self {
Self::from_bits_truncate(value)
}
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[doc(alias = "GLogLevelFlags")]
pub struct LogLevelFlags: u32 {
#[doc(alias = "G_LOG_FLAG_RECURSION")]
const FLAG_RECURSION = ffi::G_LOG_FLAG_RECURSION as _;
#[doc(alias = "G_LOG_FLAG_FATAL")]
const FLAG_FATAL = ffi::G_LOG_FLAG_FATAL as _;
#[doc(alias = "G_LOG_LEVEL_ERROR")]
const LEVEL_ERROR = ffi::G_LOG_LEVEL_ERROR as _;
#[doc(alias = "G_LOG_LEVEL_CRITICAL")]
const LEVEL_CRITICAL = ffi::G_LOG_LEVEL_CRITICAL as _;
#[doc(alias = "G_LOG_LEVEL_WARNING")]
const LEVEL_WARNING = ffi::G_LOG_LEVEL_WARNING as _;
#[doc(alias = "G_LOG_LEVEL_MESSAGE")]
const LEVEL_MESSAGE = ffi::G_LOG_LEVEL_MESSAGE as _;
#[doc(alias = "G_LOG_LEVEL_INFO")]
const LEVEL_INFO = ffi::G_LOG_LEVEL_INFO as _;
#[doc(alias = "G_LOG_LEVEL_DEBUG")]
const LEVEL_DEBUG = ffi::G_LOG_LEVEL_DEBUG as _;
#[doc(alias = "G_LOG_LEVEL_MASK")]
const LEVEL_MASK = ffi::G_LOG_LEVEL_MASK as _;
}
}
impl fmt::Display for LogLevelFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
#[doc(hidden)]
impl IntoGlib for LogLevelFlags {
type GlibType = ffi::GLogLevelFlags;
#[inline]
fn into_glib(self) -> ffi::GLogLevelFlags {
self.bits()
}
}
#[doc(hidden)]
impl FromGlib<ffi::GLogLevelFlags> for LogLevelFlags {
#[inline]
unsafe fn from_glib(value: ffi::GLogLevelFlags) -> Self {
Self::from_bits_truncate(value)
}
}
#[cfg(feature = "v2_72")]
bitflags! {
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[doc(alias = "GMainContextFlags")]
pub struct MainContextFlags: u32 {
#[doc(alias = "G_MAIN_CONTEXT_FLAGS_NONE")]
const NONE = ffi::G_MAIN_CONTEXT_FLAGS_NONE as _;
#[doc(alias = "G_MAIN_CONTEXT_FLAGS_OWNERLESS_POLLING")]
const OWNERLESS_POLLING = ffi::G_MAIN_CONTEXT_FLAGS_OWNERLESS_POLLING as _;
}
}
#[cfg(feature = "v2_72")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
impl fmt::Display for MainContextFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
#[cfg(feature = "v2_72")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
#[doc(hidden)]
impl IntoGlib for MainContextFlags {
type GlibType = ffi::GMainContextFlags;
#[inline]
fn into_glib(self) -> ffi::GMainContextFlags {
self.bits()
}
}
#[cfg(feature = "v2_72")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
#[doc(hidden)]
impl FromGlib<ffi::GMainContextFlags> for MainContextFlags {
#[inline]
unsafe fn from_glib(value: ffi::GMainContextFlags) -> Self {
Self::from_bits_truncate(value)
}
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[doc(alias = "GOptionFlags")]
pub struct OptionFlags: u32 {
#[doc(alias = "G_OPTION_FLAG_NONE")]
const NONE = ffi::G_OPTION_FLAG_NONE as _;
#[doc(alias = "G_OPTION_FLAG_HIDDEN")]
const HIDDEN = ffi::G_OPTION_FLAG_HIDDEN as _;
#[doc(alias = "G_OPTION_FLAG_IN_MAIN")]
const IN_MAIN = ffi::G_OPTION_FLAG_IN_MAIN as _;
#[doc(alias = "G_OPTION_FLAG_REVERSE")]
const REVERSE = ffi::G_OPTION_FLAG_REVERSE as _;
#[doc(alias = "G_OPTION_FLAG_NO_ARG")]
const NO_ARG = ffi::G_OPTION_FLAG_NO_ARG as _;
#[doc(alias = "G_OPTION_FLAG_FILENAME")]
const FILENAME = ffi::G_OPTION_FLAG_FILENAME as _;
#[doc(alias = "G_OPTION_FLAG_OPTIONAL_ARG")]
const OPTIONAL_ARG = ffi::G_OPTION_FLAG_OPTIONAL_ARG as _;
#[doc(alias = "G_OPTION_FLAG_NOALIAS")]
const NOALIAS = ffi::G_OPTION_FLAG_NOALIAS as _;
}
}
impl fmt::Display for OptionFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
#[doc(hidden)]
impl IntoGlib for OptionFlags {
type GlibType = ffi::GOptionFlags;
#[inline]
fn into_glib(self) -> ffi::GOptionFlags {
self.bits()
}
}
#[doc(hidden)]
impl FromGlib<ffi::GOptionFlags> for OptionFlags {
#[inline]
unsafe fn from_glib(value: ffi::GOptionFlags) -> Self {
Self::from_bits_truncate(value)
}
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[doc(alias = "GSpawnFlags")]
pub struct SpawnFlags: u32 {
#[doc(alias = "G_SPAWN_DEFAULT")]
const DEFAULT = ffi::G_SPAWN_DEFAULT as _;
#[doc(alias = "G_SPAWN_LEAVE_DESCRIPTORS_OPEN")]
const LEAVE_DESCRIPTORS_OPEN = ffi::G_SPAWN_LEAVE_DESCRIPTORS_OPEN as _;
#[doc(alias = "G_SPAWN_DO_NOT_REAP_CHILD")]
const DO_NOT_REAP_CHILD = ffi::G_SPAWN_DO_NOT_REAP_CHILD as _;
#[doc(alias = "G_SPAWN_SEARCH_PATH")]
const SEARCH_PATH = ffi::G_SPAWN_SEARCH_PATH as _;
#[doc(alias = "G_SPAWN_STDOUT_TO_DEV_NULL")]
const STDOUT_TO_DEV_NULL = ffi::G_SPAWN_STDOUT_TO_DEV_NULL as _;
#[doc(alias = "G_SPAWN_STDERR_TO_DEV_NULL")]
const STDERR_TO_DEV_NULL = ffi::G_SPAWN_STDERR_TO_DEV_NULL as _;
#[doc(alias = "G_SPAWN_CHILD_INHERITS_STDIN")]
const CHILD_INHERITS_STDIN = ffi::G_SPAWN_CHILD_INHERITS_STDIN as _;
#[doc(alias = "G_SPAWN_FILE_AND_ARGV_ZERO")]
const FILE_AND_ARGV_ZERO = ffi::G_SPAWN_FILE_AND_ARGV_ZERO as _;
#[doc(alias = "G_SPAWN_SEARCH_PATH_FROM_ENVP")]
const SEARCH_PATH_FROM_ENVP = ffi::G_SPAWN_SEARCH_PATH_FROM_ENVP as _;
#[doc(alias = "G_SPAWN_CLOEXEC_PIPES")]
const CLOEXEC_PIPES = ffi::G_SPAWN_CLOEXEC_PIPES as _;
#[cfg(feature = "v2_74")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
#[doc(alias = "G_SPAWN_CHILD_INHERITS_STDOUT")]
const CHILD_INHERITS_STDOUT = ffi::G_SPAWN_CHILD_INHERITS_STDOUT as _;
#[cfg(feature = "v2_74")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
#[doc(alias = "G_SPAWN_CHILD_INHERITS_STDERR")]
const CHILD_INHERITS_STDERR = ffi::G_SPAWN_CHILD_INHERITS_STDERR as _;
#[cfg(feature = "v2_74")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
#[doc(alias = "G_SPAWN_STDIN_FROM_DEV_NULL")]
const STDIN_FROM_DEV_NULL = ffi::G_SPAWN_STDIN_FROM_DEV_NULL as _;
}
}
impl fmt::Display for SpawnFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
#[doc(hidden)]
impl IntoGlib for SpawnFlags {
type GlibType = ffi::GSpawnFlags;
#[inline]
fn into_glib(self) -> ffi::GSpawnFlags {
self.bits()
}
}
#[doc(hidden)]
impl FromGlib<ffi::GSpawnFlags> for SpawnFlags {
#[inline]
unsafe fn from_glib(value: ffi::GSpawnFlags) -> Self {
Self::from_bits_truncate(value)
}
}
#[cfg(feature = "v2_66")]
bitflags! {
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[doc(alias = "GUriFlags")]
pub struct UriFlags: u32 {
#[doc(alias = "G_URI_FLAGS_NONE")]
const NONE = ffi::G_URI_FLAGS_NONE as _;
#[doc(alias = "G_URI_FLAGS_PARSE_RELAXED")]
const PARSE_RELAXED = ffi::G_URI_FLAGS_PARSE_RELAXED as _;
#[doc(alias = "G_URI_FLAGS_HAS_PASSWORD")]
const HAS_PASSWORD = ffi::G_URI_FLAGS_HAS_PASSWORD as _;
#[doc(alias = "G_URI_FLAGS_HAS_AUTH_PARAMS")]
const HAS_AUTH_PARAMS = ffi::G_URI_FLAGS_HAS_AUTH_PARAMS as _;
#[doc(alias = "G_URI_FLAGS_ENCODED")]
const ENCODED = ffi::G_URI_FLAGS_ENCODED as _;
#[doc(alias = "G_URI_FLAGS_NON_DNS")]
const NON_DNS = ffi::G_URI_FLAGS_NON_DNS as _;
#[doc(alias = "G_URI_FLAGS_ENCODED_QUERY")]
const ENCODED_QUERY = ffi::G_URI_FLAGS_ENCODED_QUERY as _;
#[doc(alias = "G_URI_FLAGS_ENCODED_PATH")]
const ENCODED_PATH = ffi::G_URI_FLAGS_ENCODED_PATH as _;
#[doc(alias = "G_URI_FLAGS_ENCODED_FRAGMENT")]
const ENCODED_FRAGMENT = ffi::G_URI_FLAGS_ENCODED_FRAGMENT as _;
#[doc(alias = "G_URI_FLAGS_SCHEME_NORMALIZE")]
const SCHEME_NORMALIZE = ffi::G_URI_FLAGS_SCHEME_NORMALIZE as _;
}
}
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
impl fmt::Display for UriFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
#[doc(hidden)]
impl IntoGlib for UriFlags {
type GlibType = ffi::GUriFlags;
#[inline]
fn into_glib(self) -> ffi::GUriFlags {
self.bits()
}
}
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
#[doc(hidden)]
impl FromGlib<ffi::GUriFlags> for UriFlags {
#[inline]
unsafe fn from_glib(value: ffi::GUriFlags) -> Self {
Self::from_bits_truncate(value)
}
}
#[cfg(feature = "v2_66")]
bitflags! {
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[doc(alias = "GUriHideFlags")]
pub struct UriHideFlags: u32 {
#[doc(alias = "G_URI_HIDE_NONE")]
const NONE = ffi::G_URI_HIDE_NONE as _;
#[doc(alias = "G_URI_HIDE_USERINFO")]
const USERINFO = ffi::G_URI_HIDE_USERINFO as _;
#[doc(alias = "G_URI_HIDE_PASSWORD")]
const PASSWORD = ffi::G_URI_HIDE_PASSWORD as _;
#[doc(alias = "G_URI_HIDE_AUTH_PARAMS")]
const AUTH_PARAMS = ffi::G_URI_HIDE_AUTH_PARAMS as _;
#[doc(alias = "G_URI_HIDE_QUERY")]
const QUERY = ffi::G_URI_HIDE_QUERY as _;
#[doc(alias = "G_URI_HIDE_FRAGMENT")]
const FRAGMENT = ffi::G_URI_HIDE_FRAGMENT as _;
}
}
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
impl fmt::Display for UriHideFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
#[doc(hidden)]
impl IntoGlib for UriHideFlags {
type GlibType = ffi::GUriHideFlags;
#[inline]
fn into_glib(self) -> ffi::GUriHideFlags {
self.bits()
}
}
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
#[doc(hidden)]
impl FromGlib<ffi::GUriHideFlags> for UriHideFlags {
#[inline]
unsafe fn from_glib(value: ffi::GUriHideFlags) -> Self {
Self::from_bits_truncate(value)
}
}
#[cfg(feature = "v2_66")]
bitflags! {
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[doc(alias = "GUriParamsFlags")]
pub struct UriParamsFlags: u32 {
#[doc(alias = "G_URI_PARAMS_NONE")]
const NONE = ffi::G_URI_PARAMS_NONE as _;
#[doc(alias = "G_URI_PARAMS_CASE_INSENSITIVE")]
const CASE_INSENSITIVE = ffi::G_URI_PARAMS_CASE_INSENSITIVE as _;
#[doc(alias = "G_URI_PARAMS_WWW_FORM")]
const WWW_FORM = ffi::G_URI_PARAMS_WWW_FORM as _;
#[doc(alias = "G_URI_PARAMS_PARSE_RELAXED")]
const PARSE_RELAXED = ffi::G_URI_PARAMS_PARSE_RELAXED as _;
}
}
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
impl fmt::Display for UriParamsFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
#[doc(hidden)]
impl IntoGlib for UriParamsFlags {
type GlibType = ffi::GUriParamsFlags;
#[inline]
fn into_glib(self) -> ffi::GUriParamsFlags {
self.bits()
}
}
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
#[doc(hidden)]
impl FromGlib<ffi::GUriParamsFlags> for UriParamsFlags {
#[inline]
unsafe fn from_glib(value: ffi::GUriParamsFlags) -> Self {
Self::from_bits_truncate(value)
}
}
+863
View File
@@ -0,0 +1,863 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
use crate::FileSetContentsFlags;
use crate::{
translate::*, Bytes, ChecksumType, Error, FileTest, FormatSizeFlags, Pid, Source, SpawnFlags,
UnicodeScript, UserDirectory,
};
use std::{boxed::Box as Box_, mem, ptr};
#[doc(alias = "g_access")]
pub fn access(filename: impl AsRef<std::path::Path>, mode: i32) -> i32 {
unsafe { ffi::g_access(filename.as_ref().to_glib_none().0, mode) }
}
#[doc(alias = "g_base64_decode")]
pub fn base64_decode(text: &str) -> Vec<u8> {
unsafe {
let mut out_len = mem::MaybeUninit::uninit();
let ret = FromGlibContainer::from_glib_full_num(
ffi::g_base64_decode(text.to_glib_none().0, out_len.as_mut_ptr()),
out_len.assume_init() as _,
);
ret
}
}
//#[doc(alias = "g_base64_decode_inplace")]
//pub fn base64_decode_inplace(text: /*Unimplemented*/Vec<u8>) -> u8 {
// unsafe { TODO: call ffi:g_base64_decode_inplace() }
//}
//#[doc(alias = "g_base64_decode_step")]
//pub fn base64_decode_step(in_: &[u8], out: Vec<u8>, state: &mut i32, save: &mut u32) -> usize {
// unsafe { TODO: call ffi:g_base64_decode_step() }
//}
#[doc(alias = "g_base64_encode")]
pub fn base64_encode(data: &[u8]) -> crate::GString {
let len = data.len() as _;
unsafe { from_glib_full(ffi::g_base64_encode(data.to_glib_none().0, len)) }
}
//#[doc(alias = "g_base64_encode_close")]
//pub fn base64_encode_close(break_lines: bool, out: Vec<u8>, state: &mut i32, save: &mut i32) -> usize {
// unsafe { TODO: call ffi:g_base64_encode_close() }
//}
//#[doc(alias = "g_base64_encode_step")]
//pub fn base64_encode_step(in_: &[u8], break_lines: bool, out: Vec<u8>, state: &mut i32, save: &mut i32) -> usize {
// unsafe { TODO: call ffi:g_base64_encode_step() }
//}
#[doc(alias = "glib_check_version")]
pub fn check_version(
required_major: u32,
required_minor: u32,
required_micro: u32,
) -> Option<crate::GString> {
unsafe {
from_glib_none(ffi::glib_check_version(
required_major,
required_minor,
required_micro,
))
}
}
#[doc(alias = "g_compute_checksum_for_bytes")]
pub fn compute_checksum_for_bytes(
checksum_type: ChecksumType,
data: &Bytes,
) -> Option<crate::GString> {
unsafe {
from_glib_full(ffi::g_compute_checksum_for_bytes(
checksum_type.into_glib(),
data.to_glib_none().0,
))
}
}
#[doc(alias = "g_compute_checksum_for_data")]
pub fn compute_checksum_for_data(
checksum_type: ChecksumType,
data: &[u8],
) -> Option<crate::GString> {
let length = data.len() as _;
unsafe {
from_glib_full(ffi::g_compute_checksum_for_data(
checksum_type.into_glib(),
data.to_glib_none().0,
length,
))
}
}
#[doc(alias = "g_compute_hmac_for_bytes")]
pub fn compute_hmac_for_bytes(
digest_type: ChecksumType,
key: &Bytes,
data: &Bytes,
) -> crate::GString {
unsafe {
from_glib_full(ffi::g_compute_hmac_for_bytes(
digest_type.into_glib(),
key.to_glib_none().0,
data.to_glib_none().0,
))
}
}
#[doc(alias = "g_compute_hmac_for_data")]
pub fn compute_hmac_for_data(digest_type: ChecksumType, key: &[u8], data: &[u8]) -> crate::GString {
let key_len = key.len() as _;
let length = data.len() as _;
unsafe {
from_glib_full(ffi::g_compute_hmac_for_data(
digest_type.into_glib(),
key.to_glib_none().0,
key_len,
data.to_glib_none().0,
length,
))
}
}
#[doc(alias = "g_dcgettext")]
pub fn dcgettext(domain: Option<&str>, msgid: &str, category: i32) -> crate::GString {
unsafe {
from_glib_none(ffi::g_dcgettext(
domain.to_glib_none().0,
msgid.to_glib_none().0,
category,
))
}
}
#[doc(alias = "g_dgettext")]
pub fn dgettext(domain: Option<&str>, msgid: &str) -> crate::GString {
unsafe {
from_glib_none(ffi::g_dgettext(
domain.to_glib_none().0,
msgid.to_glib_none().0,
))
}
}
#[doc(alias = "g_dngettext")]
pub fn dngettext(
domain: Option<&str>,
msgid: &str,
msgid_plural: &str,
n: libc::c_ulong,
) -> crate::GString {
unsafe {
from_glib_none(ffi::g_dngettext(
domain.to_glib_none().0,
msgid.to_glib_none().0,
msgid_plural.to_glib_none().0,
n,
))
}
}
#[doc(alias = "g_dpgettext")]
pub fn dpgettext(domain: Option<&str>, msgctxtid: &str, msgidoffset: usize) -> crate::GString {
unsafe {
from_glib_none(ffi::g_dpgettext(
domain.to_glib_none().0,
msgctxtid.to_glib_none().0,
msgidoffset,
))
}
}
#[doc(alias = "g_dpgettext2")]
pub fn dpgettext2(domain: Option<&str>, context: &str, msgid: &str) -> crate::GString {
unsafe {
from_glib_none(ffi::g_dpgettext2(
domain.to_glib_none().0,
context.to_glib_none().0,
msgid.to_glib_none().0,
))
}
}
#[doc(alias = "g_file_set_contents")]
pub fn file_set_contents(
filename: impl AsRef<std::path::Path>,
contents: &[u8],
) -> Result<(), crate::Error> {
let length = contents.len() as _;
unsafe {
let mut error = ptr::null_mut();
let is_ok = ffi::g_file_set_contents(
filename.as_ref().to_glib_none().0,
contents.to_glib_none().0,
length,
&mut error,
);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
#[doc(alias = "g_file_set_contents_full")]
pub fn file_set_contents_full(
filename: impl AsRef<std::path::Path>,
contents: &[u8],
flags: FileSetContentsFlags,
mode: i32,
) -> Result<(), crate::Error> {
let length = contents.len() as _;
unsafe {
let mut error = ptr::null_mut();
let is_ok = ffi::g_file_set_contents_full(
filename.as_ref().to_glib_none().0,
contents.to_glib_none().0,
length,
flags.into_glib(),
mode,
&mut error,
);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_file_test")]
#[allow(dead_code)]
pub(crate) fn file_test(filename: impl AsRef<std::path::Path>, test: FileTest) -> bool {
unsafe {
from_glib(ffi::g_file_test(
filename.as_ref().to_glib_none().0,
test.into_glib(),
))
}
}
#[doc(alias = "g_filename_display_basename")]
pub fn filename_display_basename(filename: impl AsRef<std::path::Path>) -> crate::GString {
unsafe {
from_glib_full(ffi::g_filename_display_basename(
filename.as_ref().to_glib_none().0,
))
}
}
#[doc(alias = "g_filename_display_name")]
pub fn filename_display_name(filename: impl AsRef<std::path::Path>) -> crate::GString {
unsafe {
from_glib_full(ffi::g_filename_display_name(
filename.as_ref().to_glib_none().0,
))
}
}
#[doc(alias = "g_filename_from_uri")]
pub fn filename_from_uri(
uri: &str,
) -> Result<(std::path::PathBuf, Option<crate::GString>), crate::Error> {
unsafe {
let mut hostname = ptr::null_mut();
let mut error = ptr::null_mut();
let ret = ffi::g_filename_from_uri(uri.to_glib_none().0, &mut hostname, &mut error);
if error.is_null() {
Ok((from_glib_full(ret), from_glib_full(hostname)))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_filename_to_uri")]
pub fn filename_to_uri(
filename: impl AsRef<std::path::Path>,
hostname: Option<&str>,
) -> Result<crate::GString, crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_filename_to_uri(
filename.as_ref().to_glib_none().0,
hostname.to_glib_none().0,
&mut error,
);
if error.is_null() {
Ok(from_glib_full(ret))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_find_program_in_path")]
pub fn find_program_in_path(program: impl AsRef<std::path::Path>) -> Option<std::path::PathBuf> {
unsafe {
from_glib_full(ffi::g_find_program_in_path(
program.as_ref().to_glib_none().0,
))
}
}
#[doc(alias = "g_format_size")]
pub fn format_size(size: u64) -> crate::GString {
unsafe { from_glib_full(ffi::g_format_size(size)) }
}
#[doc(alias = "g_format_size_full")]
pub fn format_size_full(size: u64, flags: FormatSizeFlags) -> crate::GString {
unsafe { from_glib_full(ffi::g_format_size_full(size, flags.into_glib())) }
}
#[doc(alias = "g_get_application_name")]
#[doc(alias = "get_application_name")]
pub fn application_name() -> Option<crate::GString> {
unsafe { from_glib_none(ffi::g_get_application_name()) }
}
#[doc(alias = "g_get_codeset")]
#[doc(alias = "get_codeset")]
pub fn codeset() -> crate::GString {
unsafe { from_glib_full(ffi::g_get_codeset()) }
}
#[cfg(feature = "v2_62")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_62")))]
#[doc(alias = "g_get_console_charset")]
#[doc(alias = "get_console_charset")]
pub fn console_charset() -> Option<crate::GString> {
unsafe {
let mut charset = ptr::null();
let ret = from_glib(ffi::g_get_console_charset(&mut charset));
if ret {
Some(from_glib_none(charset))
} else {
None
}
}
}
#[doc(alias = "g_get_current_dir")]
#[doc(alias = "get_current_dir")]
pub fn current_dir() -> std::path::PathBuf {
unsafe { from_glib_full(ffi::g_get_current_dir()) }
}
#[doc(alias = "g_get_environ")]
#[doc(alias = "get_environ")]
pub fn environ() -> Vec<std::ffi::OsString> {
unsafe { FromGlibPtrContainer::from_glib_full(ffi::g_get_environ()) }
}
#[doc(alias = "g_get_home_dir")]
#[doc(alias = "get_home_dir")]
pub fn home_dir() -> std::path::PathBuf {
unsafe { from_glib_none(ffi::g_get_home_dir()) }
}
#[doc(alias = "g_get_host_name")]
#[doc(alias = "get_host_name")]
pub fn host_name() -> crate::GString {
unsafe { from_glib_none(ffi::g_get_host_name()) }
}
#[doc(alias = "g_get_language_names")]
#[doc(alias = "get_language_names")]
pub fn language_names() -> Vec<crate::GString> {
unsafe { FromGlibPtrContainer::from_glib_none(ffi::g_get_language_names()) }
}
#[cfg(feature = "v2_58")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_58")))]
#[doc(alias = "g_get_language_names_with_category")]
#[doc(alias = "get_language_names_with_category")]
pub fn language_names_with_category(category_name: &str) -> Vec<crate::GString> {
unsafe {
FromGlibPtrContainer::from_glib_none(ffi::g_get_language_names_with_category(
category_name.to_glib_none().0,
))
}
}
#[doc(alias = "g_get_locale_variants")]
#[doc(alias = "get_locale_variants")]
pub fn locale_variants(locale: &str) -> Vec<crate::GString> {
unsafe {
FromGlibPtrContainer::from_glib_full(ffi::g_get_locale_variants(locale.to_glib_none().0))
}
}
#[doc(alias = "g_get_monotonic_time")]
#[doc(alias = "get_monotonic_time")]
pub fn monotonic_time() -> i64 {
unsafe { ffi::g_get_monotonic_time() }
}
#[doc(alias = "g_get_num_processors")]
#[doc(alias = "get_num_processors")]
pub fn num_processors() -> u32 {
unsafe { ffi::g_get_num_processors() }
}
#[cfg(feature = "v2_64")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_64")))]
#[doc(alias = "g_get_os_info")]
#[doc(alias = "get_os_info")]
pub fn os_info(key_name: &str) -> Option<crate::GString> {
unsafe { from_glib_full(ffi::g_get_os_info(key_name.to_glib_none().0)) }
}
#[doc(alias = "g_get_real_name")]
#[doc(alias = "get_real_name")]
pub fn real_name() -> std::ffi::OsString {
unsafe { from_glib_none(ffi::g_get_real_name()) }
}
#[doc(alias = "g_get_real_time")]
#[doc(alias = "get_real_time")]
pub fn real_time() -> i64 {
unsafe { ffi::g_get_real_time() }
}
#[doc(alias = "g_get_system_config_dirs")]
#[doc(alias = "get_system_config_dirs")]
pub fn system_config_dirs() -> Vec<std::path::PathBuf> {
unsafe { FromGlibPtrContainer::from_glib_none(ffi::g_get_system_config_dirs()) }
}
#[doc(alias = "g_get_system_data_dirs")]
#[doc(alias = "get_system_data_dirs")]
pub fn system_data_dirs() -> Vec<std::path::PathBuf> {
unsafe { FromGlibPtrContainer::from_glib_none(ffi::g_get_system_data_dirs()) }
}
#[doc(alias = "g_get_tmp_dir")]
#[doc(alias = "get_tmp_dir")]
pub fn tmp_dir() -> std::path::PathBuf {
unsafe { from_glib_none(ffi::g_get_tmp_dir()) }
}
#[doc(alias = "g_get_user_cache_dir")]
#[doc(alias = "get_user_cache_dir")]
pub fn user_cache_dir() -> std::path::PathBuf {
unsafe { from_glib_none(ffi::g_get_user_cache_dir()) }
}
#[doc(alias = "g_get_user_config_dir")]
#[doc(alias = "get_user_config_dir")]
pub fn user_config_dir() -> std::path::PathBuf {
unsafe { from_glib_none(ffi::g_get_user_config_dir()) }
}
#[doc(alias = "g_get_user_data_dir")]
#[doc(alias = "get_user_data_dir")]
pub fn user_data_dir() -> std::path::PathBuf {
unsafe { from_glib_none(ffi::g_get_user_data_dir()) }
}
#[doc(alias = "g_get_user_name")]
#[doc(alias = "get_user_name")]
pub fn user_name() -> std::ffi::OsString {
unsafe { from_glib_none(ffi::g_get_user_name()) }
}
#[doc(alias = "g_get_user_runtime_dir")]
#[doc(alias = "get_user_runtime_dir")]
pub fn user_runtime_dir() -> std::path::PathBuf {
unsafe { from_glib_none(ffi::g_get_user_runtime_dir()) }
}
#[doc(alias = "g_get_user_special_dir")]
#[doc(alias = "get_user_special_dir")]
pub fn user_special_dir(directory: UserDirectory) -> Option<std::path::PathBuf> {
unsafe { from_glib_none(ffi::g_get_user_special_dir(directory.into_glib())) }
}
#[cfg(feature = "v2_72")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
#[doc(alias = "g_get_user_state_dir")]
#[doc(alias = "get_user_state_dir")]
pub fn user_state_dir() -> std::path::PathBuf {
unsafe { from_glib_none(ffi::g_get_user_state_dir()) }
}
#[doc(alias = "g_getenv")]
pub fn getenv(variable: impl AsRef<std::ffi::OsStr>) -> Option<std::ffi::OsString> {
unsafe { from_glib_none(ffi::g_getenv(variable.as_ref().to_glib_none().0)) }
}
#[doc(alias = "g_hostname_is_ascii_encoded")]
pub fn hostname_is_ascii_encoded(hostname: &str) -> bool {
unsafe { from_glib(ffi::g_hostname_is_ascii_encoded(hostname.to_glib_none().0)) }
}
#[doc(alias = "g_hostname_is_ip_address")]
pub fn hostname_is_ip_address(hostname: &str) -> bool {
unsafe { from_glib(ffi::g_hostname_is_ip_address(hostname.to_glib_none().0)) }
}
#[doc(alias = "g_hostname_is_non_ascii")]
pub fn hostname_is_non_ascii(hostname: &str) -> bool {
unsafe { from_glib(ffi::g_hostname_is_non_ascii(hostname.to_glib_none().0)) }
}
#[doc(alias = "g_hostname_to_ascii")]
pub fn hostname_to_ascii(hostname: &str) -> Option<crate::GString> {
unsafe { from_glib_full(ffi::g_hostname_to_ascii(hostname.to_glib_none().0)) }
}
#[doc(alias = "g_hostname_to_unicode")]
pub fn hostname_to_unicode(hostname: &str) -> Option<crate::GString> {
unsafe { from_glib_full(ffi::g_hostname_to_unicode(hostname.to_glib_none().0)) }
}
#[doc(alias = "g_listenv")]
pub fn listenv() -> Vec<std::ffi::OsString> {
unsafe { FromGlibPtrContainer::from_glib_full(ffi::g_listenv()) }
}
#[doc(alias = "g_main_current_source")]
pub fn main_current_source() -> Option<Source> {
unsafe { from_glib_none(ffi::g_main_current_source()) }
}
#[doc(alias = "g_main_depth")]
pub fn main_depth() -> i32 {
unsafe { ffi::g_main_depth() }
}
#[doc(alias = "g_markup_escape_text")]
pub fn markup_escape_text(text: &str) -> crate::GString {
let length = text.len() as _;
unsafe { from_glib_full(ffi::g_markup_escape_text(text.to_glib_none().0, length)) }
}
#[doc(alias = "g_mkdir_with_parents")]
pub fn mkdir_with_parents(pathname: impl AsRef<std::path::Path>, mode: i32) -> i32 {
unsafe { ffi::g_mkdir_with_parents(pathname.as_ref().to_glib_none().0, mode) }
}
#[doc(alias = "g_on_error_query")]
pub fn on_error_query(prg_name: &str) {
unsafe {
ffi::g_on_error_query(prg_name.to_glib_none().0);
}
}
#[doc(alias = "g_on_error_stack_trace")]
pub fn on_error_stack_trace(prg_name: &str) {
unsafe {
ffi::g_on_error_stack_trace(prg_name.to_glib_none().0);
}
}
#[doc(alias = "g_path_get_basename")]
#[allow(dead_code)]
pub(crate) fn path_get_basename(file_name: impl AsRef<std::path::Path>) -> std::path::PathBuf {
unsafe {
from_glib_full(ffi::g_path_get_basename(
file_name.as_ref().to_glib_none().0,
))
}
}
#[doc(alias = "g_path_get_dirname")]
#[allow(dead_code)]
pub(crate) fn path_get_dirname(file_name: impl AsRef<std::path::Path>) -> std::path::PathBuf {
unsafe { from_glib_full(ffi::g_path_get_dirname(file_name.as_ref().to_glib_none().0)) }
}
//#[doc(alias = "g_poll")]
//pub fn poll(fds: /*Ignored*/&mut PollFD, nfds: u32, timeout: i32) -> i32 {
// unsafe { TODO: call ffi:g_poll() }
//}
#[doc(alias = "g_random_double")]
pub fn random_double() -> f64 {
unsafe { ffi::g_random_double() }
}
#[doc(alias = "g_random_double_range")]
pub fn random_double_range(begin: f64, end: f64) -> f64 {
unsafe { ffi::g_random_double_range(begin, end) }
}
#[doc(alias = "g_random_int")]
pub fn random_int() -> u32 {
unsafe { ffi::g_random_int() }
}
#[doc(alias = "g_random_int_range")]
pub fn random_int_range(begin: i32, end: i32) -> i32 {
unsafe { ffi::g_random_int_range(begin, end) }
}
#[doc(alias = "g_random_set_seed")]
pub fn random_set_seed(seed: u32) {
unsafe {
ffi::g_random_set_seed(seed);
}
}
#[doc(alias = "g_reload_user_special_dirs_cache")]
pub fn reload_user_special_dirs_cache() {
unsafe {
ffi::g_reload_user_special_dirs_cache();
}
}
#[doc(alias = "g_set_application_name")]
pub fn set_application_name(application_name: &str) {
unsafe {
ffi::g_set_application_name(application_name.to_glib_none().0);
}
}
#[doc(alias = "g_setenv")]
pub fn setenv(
variable: impl AsRef<std::ffi::OsStr>,
value: impl AsRef<std::ffi::OsStr>,
overwrite: bool,
) -> Result<(), crate::error::BoolError> {
unsafe {
crate::result_from_gboolean!(
ffi::g_setenv(
variable.as_ref().to_glib_none().0,
value.as_ref().to_glib_none().0,
overwrite.into_glib()
),
"Failed to set environment variable"
)
}
}
#[doc(alias = "g_shell_parse_argv")]
pub fn shell_parse_argv(
command_line: impl AsRef<std::ffi::OsStr>,
) -> Result<Vec<std::ffi::OsString>, crate::Error> {
unsafe {
let mut argcp = mem::MaybeUninit::uninit();
let mut argvp = ptr::null_mut();
let mut error = ptr::null_mut();
let is_ok = ffi::g_shell_parse_argv(
command_line.as_ref().to_glib_none().0,
argcp.as_mut_ptr(),
&mut argvp,
&mut error,
);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(FromGlibContainer::from_glib_full_num(
argvp,
argcp.assume_init() as _,
))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_shell_quote")]
pub fn shell_quote(unquoted_string: impl AsRef<std::ffi::OsStr>) -> std::ffi::OsString {
unsafe {
from_glib_full(ffi::g_shell_quote(
unquoted_string.as_ref().to_glib_none().0,
))
}
}
#[doc(alias = "g_shell_unquote")]
pub fn shell_unquote(
quoted_string: impl AsRef<std::ffi::OsStr>,
) -> Result<std::ffi::OsString, crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_shell_unquote(quoted_string.as_ref().to_glib_none().0, &mut error);
if error.is_null() {
Ok(from_glib_full(ret))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_spaced_primes_closest")]
pub fn spaced_primes_closest(num: u32) -> u32 {
unsafe { ffi::g_spaced_primes_closest(num) }
}
#[doc(alias = "g_spawn_async")]
pub fn spawn_async(
working_directory: Option<impl AsRef<std::path::Path>>,
argv: &[&std::path::Path],
envp: &[&std::path::Path],
flags: SpawnFlags,
child_setup: Option<Box_<dyn FnOnce() + 'static>>,
) -> Result<Pid, crate::Error> {
let child_setup_data: Box_<Option<Box_<dyn FnOnce() + 'static>>> = Box_::new(child_setup);
unsafe extern "C" fn child_setup_func(data: ffi::gpointer) {
let callback: Box_<Option<Box_<dyn FnOnce() + 'static>>> = Box_::from_raw(data as *mut _);
let callback = (*callback).expect("cannot get closure...");
callback()
}
let child_setup = if child_setup_data.is_some() {
Some(child_setup_func as _)
} else {
None
};
let super_callback0: Box_<Option<Box_<dyn FnOnce() + 'static>>> = child_setup_data;
unsafe {
let mut child_pid = mem::MaybeUninit::uninit();
let mut error = ptr::null_mut();
let is_ok = ffi::g_spawn_async(
working_directory
.as_ref()
.map(|p| p.as_ref())
.to_glib_none()
.0,
argv.to_glib_none().0,
envp.to_glib_none().0,
flags.into_glib(),
child_setup,
Box_::into_raw(super_callback0) as *mut _,
child_pid.as_mut_ptr(),
&mut error,
);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(from_glib(child_pid.assume_init()))
} else {
Err(from_glib_full(error))
}
}
}
//#[cfg(feature = "v2_68")]
//#[cfg_attr(docsrs, doc(cfg(feature = "v2_68")))]
//#[doc(alias = "g_spawn_async_with_pipes_and_fds")]
//pub fn spawn_async_with_pipes_and_fds(working_directory: Option<impl AsRef<std::path::Path>>, argv: &[&std::path::Path], envp: &[&std::path::Path], flags: SpawnFlags, child_setup: Option<Box_<dyn FnOnce() + 'static>>, stdin_fd: i32, stdout_fd: i32, stderr_fd: i32, source_fds: &[i32], target_fds: &[i32], n_fds: usize) -> Result<(Pid, i32, i32, i32), crate::Error> {
// unsafe { TODO: call ffi:g_spawn_async_with_pipes_and_fds() }
//}
#[cfg_attr(feature = "v2_70", deprecated = "Since 2.70")]
#[allow(deprecated)]
#[doc(alias = "g_spawn_check_exit_status")]
pub fn spawn_check_exit_status(wait_status: i32) -> Result<(), crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let is_ok = ffi::g_spawn_check_exit_status(wait_status, &mut error);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
#[cfg(feature = "v2_70")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_70")))]
#[doc(alias = "g_spawn_check_wait_status")]
pub fn spawn_check_wait_status(wait_status: i32) -> Result<(), crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let is_ok = ffi::g_spawn_check_wait_status(wait_status, &mut error);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
#[cfg(unix)]
#[cfg_attr(docsrs, doc(cfg(unix)))]
#[doc(alias = "g_spawn_command_line_async")]
pub fn spawn_command_line_async(
command_line: impl AsRef<std::ffi::OsStr>,
) -> Result<(), crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let is_ok =
ffi::g_spawn_command_line_async(command_line.as_ref().to_glib_none().0, &mut error);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
//#[doc(alias = "g_spawn_command_line_sync")]
//pub fn spawn_command_line_sync(command_line: impl AsRef<std::path::Path>, standard_output: Vec<u8>, standard_error: Vec<u8>) -> Result<i32, crate::Error> {
// unsafe { TODO: call ffi:g_spawn_command_line_sync() }
//}
//#[doc(alias = "g_spawn_sync")]
//pub fn spawn_sync(working_directory: Option<impl AsRef<std::path::Path>>, argv: &[&std::path::Path], envp: &[&std::path::Path], flags: SpawnFlags, child_setup: Option<&mut dyn (FnMut())>, standard_output: Vec<u8>, standard_error: Vec<u8>) -> Result<i32, crate::Error> {
// unsafe { TODO: call ffi:g_spawn_sync() }
//}
#[doc(alias = "g_unicode_script_from_iso15924")]
pub fn unicode_script_from_iso15924(iso15924: u32) -> UnicodeScript {
unsafe { from_glib(ffi::g_unicode_script_from_iso15924(iso15924)) }
}
#[doc(alias = "g_unicode_script_to_iso15924")]
pub fn unicode_script_to_iso15924(script: UnicodeScript) -> u32 {
unsafe { ffi::g_unicode_script_to_iso15924(script.into_glib()) }
}
//#[cfg(unix)]
//#[cfg_attr(docsrs, doc(cfg(unix)))]
//#[cfg(feature = "v2_64")]
//#[cfg_attr(docsrs, doc(cfg(feature = "v2_64")))]
//#[doc(alias = "g_unix_get_passwd_entry")]
//pub fn unix_get_passwd_entry(user_name: &str) -> Result</*Unimplemented*/Option<Basic: Pointer>, crate::Error> {
// unsafe { TODO: call ffi:g_unix_get_passwd_entry() }
//}
#[doc(alias = "g_unlink")]
pub fn unlink(filename: impl AsRef<std::path::Path>) -> i32 {
unsafe { ffi::g_unlink(filename.as_ref().to_glib_none().0) }
}
#[doc(alias = "g_unsetenv")]
pub fn unsetenv(variable: impl AsRef<std::ffi::OsStr>) {
unsafe {
ffi::g_unsetenv(variable.as_ref().to_glib_none().0);
}
}
#[doc(alias = "g_usleep")]
pub fn usleep(microseconds: libc::c_ulong) {
unsafe {
ffi::g_usleep(microseconds);
}
}
#[doc(alias = "g_uuid_string_is_valid")]
pub fn uuid_string_is_valid(str: &str) -> bool {
unsafe { from_glib(ffi::g_uuid_string_is_valid(str.to_glib_none().0)) }
}
#[doc(alias = "g_uuid_string_random")]
pub fn uuid_string_random() -> crate::GString {
unsafe { from_glib_full(ffi::g_uuid_string_random()) }
}
+487
View File
@@ -0,0 +1,487 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
use crate::{translate::*, Bytes, Error, KeyFileFlags};
use std::{mem, ptr};
crate::wrapper! {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct KeyFile(Shared<ffi::GKeyFile>);
match fn {
ref => |ptr| ffi::g_key_file_ref(ptr),
unref => |ptr| ffi::g_key_file_unref(ptr),
type_ => || ffi::g_key_file_get_type(),
}
}
impl KeyFile {
#[doc(alias = "g_key_file_new")]
pub fn new() -> KeyFile {
unsafe { from_glib_full(ffi::g_key_file_new()) }
}
#[doc(alias = "g_key_file_get_comment")]
#[doc(alias = "get_comment")]
pub fn comment(
&self,
group_name: Option<&str>,
key: Option<&str>,
) -> Result<crate::GString, crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_get_comment(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
&mut error,
);
if error.is_null() {
Ok(from_glib_full(ret))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_get_double")]
#[doc(alias = "get_double")]
pub fn double(&self, group_name: &str, key: &str) -> Result<f64, crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_get_double(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
&mut error,
);
if error.is_null() {
Ok(ret)
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_get_double_list")]
#[doc(alias = "get_double_list")]
pub fn double_list(&self, group_name: &str, key: &str) -> Result<Vec<f64>, crate::Error> {
unsafe {
let mut length = mem::MaybeUninit::uninit();
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_get_double_list(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
length.as_mut_ptr(),
&mut error,
);
if error.is_null() {
Ok(FromGlibContainer::from_glib_container_num(
ret,
length.assume_init() as _,
))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_get_int64")]
#[doc(alias = "get_int64")]
pub fn int64(&self, group_name: &str, key: &str) -> Result<i64, crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_get_int64(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
&mut error,
);
if error.is_null() {
Ok(ret)
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_get_integer")]
#[doc(alias = "get_integer")]
pub fn integer(&self, group_name: &str, key: &str) -> Result<i32, crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_get_integer(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
&mut error,
);
if error.is_null() {
Ok(ret)
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_get_integer_list")]
#[doc(alias = "get_integer_list")]
pub fn integer_list(&self, group_name: &str, key: &str) -> Result<Vec<i32>, crate::Error> {
unsafe {
let mut length = mem::MaybeUninit::uninit();
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_get_integer_list(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
length.as_mut_ptr(),
&mut error,
);
if error.is_null() {
Ok(FromGlibContainer::from_glib_container_num(
ret,
length.assume_init() as _,
))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_get_locale_for_key")]
#[doc(alias = "get_locale_for_key")]
pub fn locale_for_key(
&self,
group_name: &str,
key: &str,
locale: Option<&str>,
) -> Option<crate::GString> {
unsafe {
from_glib_full(ffi::g_key_file_get_locale_for_key(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
locale.to_glib_none().0,
))
}
}
#[doc(alias = "g_key_file_get_start_group")]
#[doc(alias = "get_start_group")]
pub fn start_group(&self) -> Option<crate::GString> {
unsafe { from_glib_full(ffi::g_key_file_get_start_group(self.to_glib_none().0)) }
}
#[doc(alias = "g_key_file_get_uint64")]
#[doc(alias = "get_uint64")]
pub fn uint64(&self, group_name: &str, key: &str) -> Result<u64, crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_get_uint64(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
&mut error,
);
if error.is_null() {
Ok(ret)
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_get_value")]
#[doc(alias = "get_value")]
pub fn value(&self, group_name: &str, key: &str) -> Result<crate::GString, crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_get_value(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
&mut error,
);
if error.is_null() {
Ok(from_glib_full(ret))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_has_group")]
pub fn has_group(&self, group_name: &str) -> bool {
unsafe {
from_glib(ffi::g_key_file_has_group(
self.to_glib_none().0,
group_name.to_glib_none().0,
))
}
}
#[doc(alias = "g_key_file_load_from_bytes")]
pub fn load_from_bytes(&self, bytes: &Bytes, flags: KeyFileFlags) -> Result<(), crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let is_ok = ffi::g_key_file_load_from_bytes(
self.to_glib_none().0,
bytes.to_glib_none().0,
flags.into_glib(),
&mut error,
);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_load_from_data")]
pub fn load_from_data(&self, data: &str, flags: KeyFileFlags) -> Result<(), crate::Error> {
let length = data.len() as _;
unsafe {
let mut error = ptr::null_mut();
let is_ok = ffi::g_key_file_load_from_data(
self.to_glib_none().0,
data.to_glib_none().0,
length,
flags.into_glib(),
&mut error,
);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_load_from_file")]
pub fn load_from_file(
&self,
file: impl AsRef<std::path::Path>,
flags: KeyFileFlags,
) -> Result<(), crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let is_ok = ffi::g_key_file_load_from_file(
self.to_glib_none().0,
file.as_ref().to_glib_none().0,
flags.into_glib(),
&mut error,
);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_remove_comment")]
pub fn remove_comment(
&self,
group_name: Option<&str>,
key: Option<&str>,
) -> Result<(), crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let is_ok = ffi::g_key_file_remove_comment(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
&mut error,
);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_remove_group")]
pub fn remove_group(&self, group_name: &str) -> Result<(), crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let is_ok = ffi::g_key_file_remove_group(
self.to_glib_none().0,
group_name.to_glib_none().0,
&mut error,
);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_remove_key")]
pub fn remove_key(&self, group_name: &str, key: &str) -> Result<(), crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let is_ok = ffi::g_key_file_remove_key(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
&mut error,
);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_set_boolean")]
pub fn set_boolean(&self, group_name: &str, key: &str, value: bool) {
unsafe {
ffi::g_key_file_set_boolean(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
value.into_glib(),
);
}
}
//#[doc(alias = "g_key_file_set_boolean_list")]
//pub fn set_boolean_list(&self, group_name: &str, key: &str, list: /*Unimplemented*/&CArray TypeId { ns_id: 0, id: 1 }) {
// unsafe { TODO: call ffi:g_key_file_set_boolean_list() }
//}
#[doc(alias = "g_key_file_set_comment")]
pub fn set_comment(
&self,
group_name: Option<&str>,
key: Option<&str>,
comment: &str,
) -> Result<(), crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let is_ok = ffi::g_key_file_set_comment(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
comment.to_glib_none().0,
&mut error,
);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_set_double")]
pub fn set_double(&self, group_name: &str, key: &str, value: f64) {
unsafe {
ffi::g_key_file_set_double(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
value,
);
}
}
#[doc(alias = "g_key_file_set_int64")]
pub fn set_int64(&self, group_name: &str, key: &str, value: i64) {
unsafe {
ffi::g_key_file_set_int64(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
value,
);
}
}
#[doc(alias = "g_key_file_set_integer")]
pub fn set_integer(&self, group_name: &str, key: &str, value: i32) {
unsafe {
ffi::g_key_file_set_integer(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
value,
);
}
}
#[doc(alias = "g_key_file_set_list_separator")]
pub fn set_list_separator(&self, separator: crate::Char) {
unsafe {
ffi::g_key_file_set_list_separator(self.to_glib_none().0, separator.into_glib());
}
}
#[doc(alias = "g_key_file_set_locale_string")]
pub fn set_locale_string(&self, group_name: &str, key: &str, locale: &str, string: &str) {
unsafe {
ffi::g_key_file_set_locale_string(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
locale.to_glib_none().0,
string.to_glib_none().0,
);
}
}
#[doc(alias = "g_key_file_set_string")]
pub fn set_string(&self, group_name: &str, key: &str, string: &str) {
unsafe {
ffi::g_key_file_set_string(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
string.to_glib_none().0,
);
}
}
#[doc(alias = "g_key_file_set_uint64")]
pub fn set_uint64(&self, group_name: &str, key: &str, value: u64) {
unsafe {
ffi::g_key_file_set_uint64(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
value,
);
}
}
#[doc(alias = "g_key_file_set_value")]
pub fn set_value(&self, group_name: &str, key: &str, value: &str) {
unsafe {
ffi::g_key_file_set_value(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
value.to_glib_none().0,
);
}
}
}
impl Default for KeyFile {
fn default() -> Self {
Self::new()
}
}
@@ -0,0 +1,142 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
use crate::translate::*;
#[cfg(feature = "v2_72")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
use crate::MainContextFlags;
crate::wrapper! {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MainContext(Shared<ffi::GMainContext>);
match fn {
ref => |ptr| ffi::g_main_context_ref(ptr),
unref => |ptr| ffi::g_main_context_unref(ptr),
type_ => || ffi::g_main_context_get_type(),
}
}
impl MainContext {
#[doc(alias = "g_main_context_new")]
pub fn new() -> MainContext {
unsafe { from_glib_full(ffi::g_main_context_new()) }
}
#[cfg(feature = "v2_72")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
#[doc(alias = "g_main_context_new_with_flags")]
#[doc(alias = "new_with_flags")]
pub fn with_flags(flags: MainContextFlags) -> MainContext {
unsafe { from_glib_full(ffi::g_main_context_new_with_flags(flags.into_glib())) }
}
//#[doc(alias = "g_main_context_add_poll")]
//pub fn add_poll(&self, fd: /*Ignored*/&mut PollFD, priority: i32) {
// unsafe { TODO: call ffi:g_main_context_add_poll() }
//}
//#[doc(alias = "g_main_context_check")]
//pub fn check(&self, max_priority: i32, fds: /*Ignored*/&[PollFD]) -> bool {
// unsafe { TODO: call ffi:g_main_context_check() }
//}
#[doc(alias = "g_main_context_dispatch")]
pub fn dispatch(&self) {
unsafe {
ffi::g_main_context_dispatch(self.to_glib_none().0);
}
}
//#[doc(alias = "g_main_context_find_source_by_funcs_user_data")]
//pub fn find_source_by_funcs_user_data(&self, funcs: /*Ignored*/&mut SourceFuncs, user_data: /*Unimplemented*/Option<Basic: Pointer>) -> Source {
// unsafe { TODO: call ffi:g_main_context_find_source_by_funcs_user_data() }
//}
//#[doc(alias = "g_main_context_find_source_by_user_data")]
//pub fn find_source_by_user_data(&self, user_data: /*Unimplemented*/Option<Basic: Pointer>) -> Source {
// unsafe { TODO: call ffi:g_main_context_find_source_by_user_data() }
//}
//#[doc(alias = "g_main_context_get_poll_func")]
//#[doc(alias = "get_poll_func")]
//pub fn poll_func(&self) -> /*Unimplemented*/Fn(/*Ignored*/PollFD, u32) -> i32 {
// unsafe { TODO: call ffi:g_main_context_get_poll_func() }
//}
#[doc(alias = "g_main_context_is_owner")]
pub fn is_owner(&self) -> bool {
unsafe { from_glib(ffi::g_main_context_is_owner(self.to_glib_none().0)) }
}
#[doc(alias = "g_main_context_iteration")]
pub fn iteration(&self, may_block: bool) -> bool {
unsafe {
from_glib(ffi::g_main_context_iteration(
self.to_glib_none().0,
may_block.into_glib(),
))
}
}
#[doc(alias = "g_main_context_pending")]
pub fn pending(&self) -> bool {
unsafe { from_glib(ffi::g_main_context_pending(self.to_glib_none().0)) }
}
//#[doc(alias = "g_main_context_query")]
//pub fn query(&self, max_priority: i32, fds: /*Ignored*/Vec<PollFD>) -> (i32, i32) {
// unsafe { TODO: call ffi:g_main_context_query() }
//}
//#[doc(alias = "g_main_context_remove_poll")]
//pub fn remove_poll(&self, fd: /*Ignored*/&mut PollFD) {
// unsafe { TODO: call ffi:g_main_context_remove_poll() }
//}
//#[doc(alias = "g_main_context_set_poll_func")]
//pub fn set_poll_func(&self, func: /*Unimplemented*/Fn(/*Ignored*/PollFD, u32) -> i32) {
// unsafe { TODO: call ffi:g_main_context_set_poll_func() }
//}
//#[cfg_attr(feature = "v2_58", deprecated = "Since 2.58")]
//#[allow(deprecated)]
//#[doc(alias = "g_main_context_wait")]
//pub fn wait(&self, cond: /*Ignored*/&mut Cond, mutex: /*Ignored*/&mut Mutex) -> bool {
// unsafe { TODO: call ffi:g_main_context_wait() }
//}
#[doc(alias = "g_main_context_wakeup")]
pub fn wakeup(&self) {
unsafe {
ffi::g_main_context_wakeup(self.to_glib_none().0);
}
}
#[doc(alias = "g_main_context_default")]
#[allow(clippy::should_implement_trait)]
pub fn default() -> MainContext {
unsafe { from_glib_none(ffi::g_main_context_default()) }
}
#[doc(alias = "g_main_context_get_thread_default")]
#[doc(alias = "get_thread_default")]
pub fn thread_default() -> Option<MainContext> {
unsafe { from_glib_none(ffi::g_main_context_get_thread_default()) }
}
#[doc(alias = "g_main_context_ref_thread_default")]
pub fn ref_thread_default() -> MainContext {
unsafe { from_glib_full(ffi::g_main_context_ref_thread_default()) }
}
}
impl Default for MainContext {
fn default() -> Self {
Self::new()
}
}
unsafe impl Send for MainContext {}
unsafe impl Sync for MainContext {}
@@ -0,0 +1,56 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
use crate::{translate::*, MainContext};
crate::wrapper! {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MainLoop(Shared<ffi::GMainLoop>);
match fn {
ref => |ptr| ffi::g_main_loop_ref(ptr),
unref => |ptr| ffi::g_main_loop_unref(ptr),
type_ => || ffi::g_main_loop_get_type(),
}
}
impl MainLoop {
#[doc(alias = "g_main_loop_new")]
pub fn new(context: Option<&MainContext>, is_running: bool) -> MainLoop {
unsafe {
from_glib_full(ffi::g_main_loop_new(
context.to_glib_none().0,
is_running.into_glib(),
))
}
}
#[doc(alias = "g_main_loop_get_context")]
#[doc(alias = "get_context")]
pub fn context(&self) -> MainContext {
unsafe { from_glib_none(ffi::g_main_loop_get_context(self.to_glib_none().0)) }
}
#[doc(alias = "g_main_loop_is_running")]
pub fn is_running(&self) -> bool {
unsafe { from_glib(ffi::g_main_loop_is_running(self.to_glib_none().0)) }
}
#[doc(alias = "g_main_loop_quit")]
pub fn quit(&self) {
unsafe {
ffi::g_main_loop_quit(self.to_glib_none().0);
}
}
#[doc(alias = "g_main_loop_run")]
pub fn run(&self) {
unsafe {
ffi::g_main_loop_run(self.to_glib_none().0);
}
}
}
unsafe impl Send for MainLoop {}
unsafe impl Sync for MainLoop {}
@@ -0,0 +1,99 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
use crate::{translate::*, Error};
use std::{mem, ptr};
crate::wrapper! {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MarkupParseContext(Shared<ffi::GMarkupParseContext>);
match fn {
ref => |ptr| ffi::g_markup_parse_context_ref(ptr),
unref => |ptr| ffi::g_markup_parse_context_unref(ptr),
type_ => || ffi::g_markup_parse_context_get_type(),
}
}
impl MarkupParseContext {
//#[doc(alias = "g_markup_parse_context_new")]
//pub fn new(parser: /*Ignored*/&MarkupParser, flags: /*Ignored*/MarkupParseFlags, user_data: /*Unimplemented*/Option<Basic: Pointer>) -> MarkupParseContext {
// unsafe { TODO: call ffi:g_markup_parse_context_new() }
//}
#[doc(alias = "g_markup_parse_context_end_parse")]
pub fn end_parse(&self) -> Result<(), crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let is_ok = ffi::g_markup_parse_context_end_parse(self.to_glib_none().0, &mut error);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_markup_parse_context_get_element")]
#[doc(alias = "get_element")]
pub fn element(&self) -> crate::GString {
unsafe {
from_glib_none(ffi::g_markup_parse_context_get_element(
self.to_glib_none().0,
))
}
}
//#[doc(alias = "g_markup_parse_context_get_element_stack")]
//#[doc(alias = "get_element_stack")]
//pub fn element_stack(&self) -> /*Unimplemented*/Vec<Basic: Pointer> {
// unsafe { TODO: call ffi:g_markup_parse_context_get_element_stack() }
//}
#[doc(alias = "g_markup_parse_context_get_position")]
#[doc(alias = "get_position")]
pub fn position(&self) -> (i32, i32) {
unsafe {
let mut line_number = mem::MaybeUninit::uninit();
let mut char_number = mem::MaybeUninit::uninit();
ffi::g_markup_parse_context_get_position(
self.to_glib_none().0,
line_number.as_mut_ptr(),
char_number.as_mut_ptr(),
);
(line_number.assume_init(), char_number.assume_init())
}
}
#[doc(alias = "g_markup_parse_context_parse")]
pub fn parse(&self, text: &str) -> Result<(), crate::Error> {
let text_len = text.len() as _;
unsafe {
let mut error = ptr::null_mut();
let is_ok = ffi::g_markup_parse_context_parse(
self.to_glib_none().0,
text.to_glib_none().0,
text_len,
&mut error,
);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
//#[doc(alias = "g_markup_parse_context_pop")]
//pub fn pop(&self) -> /*Unimplemented*/Option<Basic: Pointer> {
// unsafe { TODO: call ffi:g_markup_parse_context_pop() }
//}
//#[doc(alias = "g_markup_parse_context_push")]
//pub fn push(&self, parser: /*Ignored*/&MarkupParser, user_data: /*Unimplemented*/Option<Basic: Pointer>) {
// unsafe { TODO: call ffi:g_markup_parse_context_push() }
//}
}
+123
View File
@@ -0,0 +1,123 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
mod checksum;
pub use self::checksum::Checksum;
mod date_time;
pub use self::date_time::DateTime;
mod key_file;
pub use self::key_file::KeyFile;
mod main_context;
pub use self::main_context::MainContext;
mod main_loop;
pub use self::main_loop::MainLoop;
mod markup_parse_context;
pub use self::markup_parse_context::MarkupParseContext;
mod source;
pub use self::source::Source;
mod time_zone;
pub use self::time_zone::TimeZone;
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
mod uri;
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
pub use self::uri::Uri;
mod enums;
pub use self::enums::ChecksumType;
pub use self::enums::ConvertError;
pub use self::enums::DateMonth;
pub use self::enums::DateWeekday;
pub use self::enums::FileError;
pub use self::enums::KeyFileError;
pub use self::enums::LogWriterOutput;
pub use self::enums::MarkupError;
pub use self::enums::NormalizeMode;
pub use self::enums::OptionArg;
pub use self::enums::SeekType;
pub use self::enums::TimeType;
pub use self::enums::UnicodeBreakType;
pub use self::enums::UnicodeScript;
pub use self::enums::UnicodeType;
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
pub use self::enums::UriError;
pub use self::enums::VariantClass;
mod flags;
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
pub use self::flags::FileSetContentsFlags;
pub(crate) use self::flags::FileTest;
pub use self::flags::FormatSizeFlags;
pub use self::flags::IOCondition;
pub use self::flags::KeyFileFlags;
pub use self::flags::LogLevelFlags;
#[cfg(feature = "v2_72")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
pub use self::flags::MainContextFlags;
pub use self::flags::OptionFlags;
pub use self::flags::SpawnFlags;
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
pub use self::flags::UriFlags;
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
pub use self::flags::UriHideFlags;
#[cfg(feature = "v2_66")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
pub use self::flags::UriParamsFlags;
mod alias;
pub use self::alias::DateDay;
pub use self::alias::DateYear;
pub use self::alias::Time;
pub mod functions;
mod constants;
pub use self::constants::CSET_a_2_z;
pub use self::constants::CSET_A_2_Z;
pub use self::constants::CSET_DIGITS;
pub use self::constants::KEY_FILE_DESKTOP_GROUP;
pub use self::constants::KEY_FILE_DESKTOP_KEY_ACTIONS;
pub use self::constants::KEY_FILE_DESKTOP_KEY_CATEGORIES;
pub use self::constants::KEY_FILE_DESKTOP_KEY_COMMENT;
pub use self::constants::KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE;
pub use self::constants::KEY_FILE_DESKTOP_KEY_EXEC;
pub use self::constants::KEY_FILE_DESKTOP_KEY_GENERIC_NAME;
pub use self::constants::KEY_FILE_DESKTOP_KEY_HIDDEN;
pub use self::constants::KEY_FILE_DESKTOP_KEY_ICON;
pub use self::constants::KEY_FILE_DESKTOP_KEY_MIME_TYPE;
pub use self::constants::KEY_FILE_DESKTOP_KEY_NAME;
pub use self::constants::KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN;
pub use self::constants::KEY_FILE_DESKTOP_KEY_NO_DISPLAY;
pub use self::constants::KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN;
pub use self::constants::KEY_FILE_DESKTOP_KEY_PATH;
pub use self::constants::KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY;
pub use self::constants::KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS;
pub use self::constants::KEY_FILE_DESKTOP_KEY_TERMINAL;
pub use self::constants::KEY_FILE_DESKTOP_KEY_TRY_EXEC;
pub use self::constants::KEY_FILE_DESKTOP_KEY_TYPE;
pub use self::constants::KEY_FILE_DESKTOP_KEY_URL;
pub use self::constants::KEY_FILE_DESKTOP_KEY_VERSION;
pub use self::constants::KEY_FILE_DESKTOP_TYPE_APPLICATION;
pub use self::constants::KEY_FILE_DESKTOP_TYPE_DIRECTORY;
pub use self::constants::KEY_FILE_DESKTOP_TYPE_LINK;
pub use self::constants::OPTION_REMAINING;
pub use self::constants::STR_DELIMITERS;
#[cfg(feature = "v2_60")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_60")))]
pub use self::constants::TEST_OPTION_ISOLATE_DIRS;
pub use self::constants::URI_RESERVED_CHARS_GENERIC_DELIMITERS;
pub use self::constants::URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS;
+128
View File
@@ -0,0 +1,128 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
use crate::{translate::*, MainContext};
crate::wrapper! {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Source(Shared<ffi::GSource>);
match fn {
ref => |ptr| ffi::g_source_ref(ptr),
unref => |ptr| ffi::g_source_unref(ptr),
type_ => || ffi::g_source_get_type(),
}
}
impl Source {
//#[doc(alias = "g_source_new")]
//pub fn new(source_funcs: /*Ignored*/&mut SourceFuncs, struct_size: u32) -> Source {
// unsafe { TODO: call ffi:g_source_new() }
//}
#[doc(alias = "g_source_add_child_source")]
pub fn add_child_source(&self, child_source: &Source) {
unsafe {
ffi::g_source_add_child_source(self.to_glib_none().0, child_source.to_glib_none().0);
}
}
//#[doc(alias = "g_source_add_poll")]
//pub fn add_poll(&self, fd: /*Ignored*/&mut PollFD) {
// unsafe { TODO: call ffi:g_source_add_poll() }
//}
//#[doc(alias = "g_source_add_unix_fd")]
//pub fn add_unix_fd(&self, fd: i32, events: IOCondition) -> /*Unimplemented*/Basic: Pointer {
// unsafe { TODO: call ffi:g_source_add_unix_fd() }
//}
#[doc(alias = "g_source_destroy")]
pub fn destroy(&self) {
unsafe {
ffi::g_source_destroy(self.to_glib_none().0);
}
}
#[doc(alias = "g_source_get_can_recurse")]
#[doc(alias = "get_can_recurse")]
pub fn can_recurse(&self) -> bool {
unsafe { from_glib(ffi::g_source_get_can_recurse(self.to_glib_none().0)) }
}
#[doc(alias = "g_source_get_context")]
#[doc(alias = "get_context")]
pub fn context(&self) -> Option<MainContext> {
unsafe { from_glib_none(ffi::g_source_get_context(self.to_glib_none().0)) }
}
#[doc(alias = "g_source_get_name")]
#[doc(alias = "get_name")]
pub fn name(&self) -> Option<crate::GString> {
unsafe { from_glib_none(ffi::g_source_get_name(self.to_glib_none().0)) }
}
#[doc(alias = "g_source_get_priority")]
#[doc(alias = "get_priority")]
pub fn priority(&self) -> i32 {
unsafe { ffi::g_source_get_priority(self.to_glib_none().0) }
}
#[doc(alias = "g_source_get_ready_time")]
#[doc(alias = "get_ready_time")]
pub fn ready_time(&self) -> i64 {
unsafe { ffi::g_source_get_ready_time(self.to_glib_none().0) }
}
#[doc(alias = "g_source_get_time")]
#[doc(alias = "get_time")]
pub fn time(&self) -> i64 {
unsafe { ffi::g_source_get_time(self.to_glib_none().0) }
}
#[doc(alias = "g_source_is_destroyed")]
pub fn is_destroyed(&self) -> bool {
unsafe { from_glib(ffi::g_source_is_destroyed(self.to_glib_none().0)) }
}
//#[doc(alias = "g_source_modify_unix_fd")]
//pub fn modify_unix_fd(&self, tag: /*Unimplemented*/Basic: Pointer, new_events: IOCondition) {
// unsafe { TODO: call ffi:g_source_modify_unix_fd() }
//}
//#[doc(alias = "g_source_query_unix_fd")]
//pub fn query_unix_fd(&self, tag: /*Unimplemented*/Basic: Pointer) -> IOCondition {
// unsafe { TODO: call ffi:g_source_query_unix_fd() }
//}
#[doc(alias = "g_source_remove_child_source")]
pub fn remove_child_source(&self, child_source: &Source) {
unsafe {
ffi::g_source_remove_child_source(self.to_glib_none().0, child_source.to_glib_none().0);
}
}
//#[doc(alias = "g_source_remove_poll")]
//pub fn remove_poll(&self, fd: /*Ignored*/&mut PollFD) {
// unsafe { TODO: call ffi:g_source_remove_poll() }
//}
//#[doc(alias = "g_source_remove_unix_fd")]
//pub fn remove_unix_fd(&self, tag: /*Unimplemented*/Basic: Pointer) {
// unsafe { TODO: call ffi:g_source_remove_unix_fd() }
//}
//#[doc(alias = "g_source_remove_by_funcs_user_data")]
//pub fn remove_by_funcs_user_data(funcs: /*Ignored*/&mut SourceFuncs, user_data: /*Unimplemented*/Option<Basic: Pointer>) -> bool {
// unsafe { TODO: call ffi:g_source_remove_by_funcs_user_data() }
//}
//#[doc(alias = "g_source_remove_by_user_data")]
//pub fn remove_by_user_data(user_data: /*Unimplemented*/Option<Basic: Pointer>) -> bool {
// unsafe { TODO: call ffi:g_source_remove_by_user_data() }
//}
}
unsafe impl Send for Source {}
unsafe impl Sync for Source {}
@@ -0,0 +1,91 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
use crate::{translate::*, TimeType};
crate::wrapper! {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TimeZone(Shared<ffi::GTimeZone>);
match fn {
ref => |ptr| ffi::g_time_zone_ref(ptr),
unref => |ptr| ffi::g_time_zone_unref(ptr),
type_ => || ffi::g_time_zone_get_type(),
}
}
impl TimeZone {
#[cfg_attr(feature = "v2_68", deprecated = "Since 2.68")]
#[allow(deprecated)]
#[doc(alias = "g_time_zone_new")]
pub fn new(identifier: Option<&str>) -> TimeZone {
unsafe { from_glib_full(ffi::g_time_zone_new(identifier.to_glib_none().0)) }
}
#[cfg(feature = "v2_68")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_68")))]
#[doc(alias = "g_time_zone_new_identifier")]
#[doc(alias = "new_identifier")]
pub fn from_identifier(identifier: Option<&str>) -> Option<TimeZone> {
unsafe { from_glib_full(ffi::g_time_zone_new_identifier(identifier.to_glib_none().0)) }
}
#[doc(alias = "g_time_zone_new_local")]
#[doc(alias = "new_local")]
pub fn local() -> TimeZone {
unsafe { from_glib_full(ffi::g_time_zone_new_local()) }
}
#[cfg(feature = "v2_58")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_58")))]
#[doc(alias = "g_time_zone_new_offset")]
#[doc(alias = "new_offset")]
pub fn from_offset(seconds: i32) -> TimeZone {
unsafe { from_glib_full(ffi::g_time_zone_new_offset(seconds)) }
}
#[doc(alias = "g_time_zone_new_utc")]
#[doc(alias = "new_utc")]
pub fn utc() -> TimeZone {
unsafe { from_glib_full(ffi::g_time_zone_new_utc()) }
}
#[doc(alias = "g_time_zone_find_interval")]
pub fn find_interval(&self, type_: TimeType, time_: i64) -> i32 {
unsafe { ffi::g_time_zone_find_interval(self.to_glib_none().0, type_.into_glib(), time_) }
}
#[doc(alias = "g_time_zone_get_abbreviation")]
#[doc(alias = "get_abbreviation")]
pub fn abbreviation(&self, interval: i32) -> crate::GString {
unsafe {
from_glib_none(ffi::g_time_zone_get_abbreviation(
self.to_glib_none().0,
interval,
))
}
}
#[cfg(feature = "v2_58")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_58")))]
#[doc(alias = "g_time_zone_get_identifier")]
#[doc(alias = "get_identifier")]
pub fn identifier(&self) -> crate::GString {
unsafe { from_glib_none(ffi::g_time_zone_get_identifier(self.to_glib_none().0)) }
}
#[doc(alias = "g_time_zone_get_offset")]
#[doc(alias = "get_offset")]
pub fn offset(&self, interval: i32) -> i32 {
unsafe { ffi::g_time_zone_get_offset(self.to_glib_none().0, interval) }
}
#[doc(alias = "g_time_zone_is_dst")]
pub fn is_dst(&self, interval: i32) -> bool {
unsafe { from_glib(ffi::g_time_zone_is_dst(self.to_glib_none().0, interval)) }
}
}
unsafe impl Send for TimeZone {}
unsafe impl Sync for TimeZone {}
+535
View File
@@ -0,0 +1,535 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
use crate::{translate::*, Bytes, Error, UriFlags, UriHideFlags};
use std::{fmt, mem, ptr};
crate::wrapper! {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Uri(Shared<ffi::GUri>);
match fn {
ref => |ptr| ffi::g_uri_ref(ptr),
unref => |ptr| ffi::g_uri_unref(ptr),
type_ => || ffi::g_uri_get_type(),
}
}
impl Uri {
#[doc(alias = "g_uri_get_auth_params")]
#[doc(alias = "get_auth_params")]
pub fn auth_params(&self) -> Option<crate::GString> {
unsafe { from_glib_none(ffi::g_uri_get_auth_params(self.to_glib_none().0)) }
}
#[doc(alias = "g_uri_get_flags")]
#[doc(alias = "get_flags")]
pub fn flags(&self) -> UriFlags {
unsafe { from_glib(ffi::g_uri_get_flags(self.to_glib_none().0)) }
}
#[doc(alias = "g_uri_get_fragment")]
#[doc(alias = "get_fragment")]
pub fn fragment(&self) -> Option<crate::GString> {
unsafe { from_glib_none(ffi::g_uri_get_fragment(self.to_glib_none().0)) }
}
#[doc(alias = "g_uri_get_host")]
#[doc(alias = "get_host")]
pub fn host(&self) -> Option<crate::GString> {
unsafe { from_glib_none(ffi::g_uri_get_host(self.to_glib_none().0)) }
}
#[doc(alias = "g_uri_get_password")]
#[doc(alias = "get_password")]
pub fn password(&self) -> Option<crate::GString> {
unsafe { from_glib_none(ffi::g_uri_get_password(self.to_glib_none().0)) }
}
#[doc(alias = "g_uri_get_path")]
#[doc(alias = "get_path")]
pub fn path(&self) -> crate::GString {
unsafe { from_glib_none(ffi::g_uri_get_path(self.to_glib_none().0)) }
}
#[doc(alias = "g_uri_get_port")]
#[doc(alias = "get_port")]
pub fn port(&self) -> i32 {
unsafe { ffi::g_uri_get_port(self.to_glib_none().0) }
}
#[doc(alias = "g_uri_get_query")]
#[doc(alias = "get_query")]
pub fn query(&self) -> Option<crate::GString> {
unsafe { from_glib_none(ffi::g_uri_get_query(self.to_glib_none().0)) }
}
#[doc(alias = "g_uri_get_scheme")]
#[doc(alias = "get_scheme")]
pub fn scheme(&self) -> crate::GString {
unsafe { from_glib_none(ffi::g_uri_get_scheme(self.to_glib_none().0)) }
}
#[doc(alias = "g_uri_get_user")]
#[doc(alias = "get_user")]
pub fn user(&self) -> Option<crate::GString> {
unsafe { from_glib_none(ffi::g_uri_get_user(self.to_glib_none().0)) }
}
#[doc(alias = "g_uri_get_userinfo")]
#[doc(alias = "get_userinfo")]
pub fn userinfo(&self) -> Option<crate::GString> {
unsafe { from_glib_none(ffi::g_uri_get_userinfo(self.to_glib_none().0)) }
}
#[doc(alias = "g_uri_parse_relative")]
pub fn parse_relative(&self, uri_ref: &str, flags: UriFlags) -> Result<Uri, crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_uri_parse_relative(
self.to_glib_none().0,
uri_ref.to_glib_none().0,
flags.into_glib(),
&mut error,
);
if error.is_null() {
Ok(from_glib_full(ret))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_uri_to_string")]
#[doc(alias = "to_string")]
pub fn to_str(&self) -> crate::GString {
unsafe { from_glib_full(ffi::g_uri_to_string(self.to_glib_none().0)) }
}
#[doc(alias = "g_uri_to_string_partial")]
pub fn to_string_partial(&self, flags: UriHideFlags) -> crate::GString {
unsafe {
from_glib_full(ffi::g_uri_to_string_partial(
self.to_glib_none().0,
flags.into_glib(),
))
}
}
#[doc(alias = "g_uri_build")]
pub fn build(
flags: UriFlags,
scheme: &str,
userinfo: Option<&str>,
host: Option<&str>,
port: i32,
path: &str,
query: Option<&str>,
fragment: Option<&str>,
) -> Uri {
unsafe {
from_glib_full(ffi::g_uri_build(
flags.into_glib(),
scheme.to_glib_none().0,
userinfo.to_glib_none().0,
host.to_glib_none().0,
port,
path.to_glib_none().0,
query.to_glib_none().0,
fragment.to_glib_none().0,
))
}
}
#[doc(alias = "g_uri_build_with_user")]
pub fn build_with_user(
flags: UriFlags,
scheme: &str,
user: Option<&str>,
password: Option<&str>,
auth_params: Option<&str>,
host: Option<&str>,
port: i32,
path: &str,
query: Option<&str>,
fragment: Option<&str>,
) -> Uri {
unsafe {
from_glib_full(ffi::g_uri_build_with_user(
flags.into_glib(),
scheme.to_glib_none().0,
user.to_glib_none().0,
password.to_glib_none().0,
auth_params.to_glib_none().0,
host.to_glib_none().0,
port,
path.to_glib_none().0,
query.to_glib_none().0,
fragment.to_glib_none().0,
))
}
}
#[doc(alias = "g_uri_escape_bytes")]
pub fn escape_bytes(unescaped: &[u8], reserved_chars_allowed: Option<&str>) -> crate::GString {
let length = unescaped.len() as _;
unsafe {
from_glib_full(ffi::g_uri_escape_bytes(
unescaped.to_glib_none().0,
length,
reserved_chars_allowed.to_glib_none().0,
))
}
}
#[doc(alias = "g_uri_escape_string")]
pub fn escape_string(
unescaped: &str,
reserved_chars_allowed: Option<&str>,
allow_utf8: bool,
) -> crate::GString {
unsafe {
from_glib_full(ffi::g_uri_escape_string(
unescaped.to_glib_none().0,
reserved_chars_allowed.to_glib_none().0,
allow_utf8.into_glib(),
))
}
}
#[doc(alias = "g_uri_is_valid")]
pub fn is_valid(uri_string: &str, flags: UriFlags) -> Result<(), crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let is_ok =
ffi::g_uri_is_valid(uri_string.to_glib_none().0, flags.into_glib(), &mut error);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_uri_join")]
pub fn join(
flags: UriFlags,
scheme: Option<&str>,
userinfo: Option<&str>,
host: Option<&str>,
port: i32,
path: &str,
query: Option<&str>,
fragment: Option<&str>,
) -> crate::GString {
unsafe {
from_glib_full(ffi::g_uri_join(
flags.into_glib(),
scheme.to_glib_none().0,
userinfo.to_glib_none().0,
host.to_glib_none().0,
port,
path.to_glib_none().0,
query.to_glib_none().0,
fragment.to_glib_none().0,
))
}
}
#[doc(alias = "g_uri_join_with_user")]
pub fn join_with_user(
flags: UriFlags,
scheme: Option<&str>,
user: Option<&str>,
password: Option<&str>,
auth_params: Option<&str>,
host: Option<&str>,
port: i32,
path: &str,
query: Option<&str>,
fragment: Option<&str>,
) -> crate::GString {
unsafe {
from_glib_full(ffi::g_uri_join_with_user(
flags.into_glib(),
scheme.to_glib_none().0,
user.to_glib_none().0,
password.to_glib_none().0,
auth_params.to_glib_none().0,
host.to_glib_none().0,
port,
path.to_glib_none().0,
query.to_glib_none().0,
fragment.to_glib_none().0,
))
}
}
#[doc(alias = "g_uri_list_extract_uris")]
pub fn list_extract_uris(uri_list: &str) -> Vec<crate::GString> {
unsafe {
FromGlibPtrContainer::from_glib_full(ffi::g_uri_list_extract_uris(
uri_list.to_glib_none().0,
))
}
}
#[doc(alias = "g_uri_parse")]
pub fn parse(uri_string: &str, flags: UriFlags) -> Result<Uri, crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_uri_parse(uri_string.to_glib_none().0, flags.into_glib(), &mut error);
if error.is_null() {
Ok(from_glib_full(ret))
} else {
Err(from_glib_full(error))
}
}
}
//#[doc(alias = "g_uri_parse_params")]
//pub fn parse_params(params: &str, separators: &str, flags: UriParamsFlags) -> Result</*Unknown conversion*//*Unimplemented*/HashTable TypeId { ns_id: 0, id: 28 }/TypeId { ns_id: 0, id: 28 }, crate::Error> {
// unsafe { TODO: call ffi:g_uri_parse_params() }
//}
#[doc(alias = "g_uri_parse_scheme")]
pub fn parse_scheme(uri: &str) -> Option<crate::GString> {
unsafe { from_glib_full(ffi::g_uri_parse_scheme(uri.to_glib_none().0)) }
}
#[doc(alias = "g_uri_peek_scheme")]
pub fn peek_scheme(uri: &str) -> Option<crate::GString> {
unsafe { from_glib_none(ffi::g_uri_peek_scheme(uri.to_glib_none().0)) }
}
#[doc(alias = "g_uri_resolve_relative")]
pub fn resolve_relative(
base_uri_string: Option<&str>,
uri_ref: &str,
flags: UriFlags,
) -> Result<crate::GString, crate::Error> {
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_uri_resolve_relative(
base_uri_string.to_glib_none().0,
uri_ref.to_glib_none().0,
flags.into_glib(),
&mut error,
);
if error.is_null() {
Ok(from_glib_full(ret))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_uri_split")]
pub fn split(
uri_ref: &str,
flags: UriFlags,
) -> Result<
(
Option<crate::GString>,
Option<crate::GString>,
Option<crate::GString>,
i32,
crate::GString,
Option<crate::GString>,
Option<crate::GString>,
),
crate::Error,
> {
unsafe {
let mut scheme = ptr::null_mut();
let mut userinfo = ptr::null_mut();
let mut host = ptr::null_mut();
let mut port = mem::MaybeUninit::uninit();
let mut path = ptr::null_mut();
let mut query = ptr::null_mut();
let mut fragment = ptr::null_mut();
let mut error = ptr::null_mut();
let is_ok = ffi::g_uri_split(
uri_ref.to_glib_none().0,
flags.into_glib(),
&mut scheme,
&mut userinfo,
&mut host,
port.as_mut_ptr(),
&mut path,
&mut query,
&mut fragment,
&mut error,
);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok((
from_glib_full(scheme),
from_glib_full(userinfo),
from_glib_full(host),
port.assume_init(),
from_glib_full(path),
from_glib_full(query),
from_glib_full(fragment),
))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_uri_split_network")]
pub fn split_network(
uri_string: &str,
flags: UriFlags,
) -> Result<(Option<crate::GString>, Option<crate::GString>, i32), crate::Error> {
unsafe {
let mut scheme = ptr::null_mut();
let mut host = ptr::null_mut();
let mut port = mem::MaybeUninit::uninit();
let mut error = ptr::null_mut();
let is_ok = ffi::g_uri_split_network(
uri_string.to_glib_none().0,
flags.into_glib(),
&mut scheme,
&mut host,
port.as_mut_ptr(),
&mut error,
);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok((
from_glib_full(scheme),
from_glib_full(host),
port.assume_init(),
))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_uri_split_with_user")]
pub fn split_with_user(
uri_ref: &str,
flags: UriFlags,
) -> Result<
(
Option<crate::GString>,
Option<crate::GString>,
Option<crate::GString>,
Option<crate::GString>,
Option<crate::GString>,
i32,
crate::GString,
Option<crate::GString>,
Option<crate::GString>,
),
crate::Error,
> {
unsafe {
let mut scheme = ptr::null_mut();
let mut user = ptr::null_mut();
let mut password = ptr::null_mut();
let mut auth_params = ptr::null_mut();
let mut host = ptr::null_mut();
let mut port = mem::MaybeUninit::uninit();
let mut path = ptr::null_mut();
let mut query = ptr::null_mut();
let mut fragment = ptr::null_mut();
let mut error = ptr::null_mut();
let is_ok = ffi::g_uri_split_with_user(
uri_ref.to_glib_none().0,
flags.into_glib(),
&mut scheme,
&mut user,
&mut password,
&mut auth_params,
&mut host,
port.as_mut_ptr(),
&mut path,
&mut query,
&mut fragment,
&mut error,
);
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
if error.is_null() {
Ok((
from_glib_full(scheme),
from_glib_full(user),
from_glib_full(password),
from_glib_full(auth_params),
from_glib_full(host),
port.assume_init(),
from_glib_full(path),
from_glib_full(query),
from_glib_full(fragment),
))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_uri_unescape_bytes")]
pub fn unescape_bytes(
escaped_string: &str,
illegal_characters: Option<&str>,
) -> Result<Bytes, crate::Error> {
let length = escaped_string.len() as _;
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_uri_unescape_bytes(
escaped_string.to_glib_none().0,
length,
illegal_characters.to_glib_none().0,
&mut error,
);
if error.is_null() {
Ok(from_glib_full(ret))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_uri_unescape_segment")]
pub fn unescape_segment(
escaped_string: Option<&str>,
escaped_string_end: Option<&str>,
illegal_characters: Option<&str>,
) -> Option<crate::GString> {
unsafe {
from_glib_full(ffi::g_uri_unescape_segment(
escaped_string.to_glib_none().0,
escaped_string_end.to_glib_none().0,
illegal_characters.to_glib_none().0,
))
}
}
#[doc(alias = "g_uri_unescape_string")]
pub fn unescape_string(
escaped_string: &str,
illegal_characters: Option<&str>,
) -> Option<crate::GString> {
unsafe {
from_glib_full(ffi::g_uri_unescape_string(
escaped_string.to_glib_none().0,
illegal_characters.to_glib_none().0,
))
}
}
}
impl fmt::Display for Uri {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.to_str())
}
}
unsafe impl Send for Uri {}
unsafe impl Sync for Uri {}
@@ -0,0 +1,2 @@
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
+606
View File
@@ -0,0 +1,606 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! `IMPL` Boxed wrapper implementation.
use std::{
cmp, fmt,
hash::{Hash, Hasher},
marker::PhantomData,
ops::{Deref, DerefMut},
ptr,
};
use crate::translate::*;
// rustdoc-stripper-ignore-next
/// Wrapper implementations for Boxed types. See `wrapper!`.
#[macro_export]
macro_rules! glib_boxed_wrapper {
([$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty,
@copy $copy_arg:ident $copy_expr:expr, @free $free_arg:ident $free_expr:expr
$(, @type_ $get_type_expr:expr)?) => {
$crate::glib_boxed_wrapper!(@generic_impl [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name);
$crate::glib_boxed_wrapper!(
@memory_manager_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name,
@copy $copy_arg $copy_expr, @free $free_arg $free_expr
);
$crate::glib_boxed_wrapper!(@value_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name $(, @type_ $get_type_expr)?);
};
(@generic_impl [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty) => {
$(#[$attr])*
#[repr(transparent)]
$visibility struct $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? {
inner: $crate::boxed::Boxed<$ffi_name, Self>,
}
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $name $(<$($generic),+>)? {
#[doc = "Return the inner pointer to the underlying C value."]
#[inline]
pub fn as_ptr(&self) -> *mut $ffi_name {
unsafe { *(self as *const Self as *const *const $ffi_name) as *mut $ffi_name }
}
#[doc = "Borrows the underlying C value."]
#[inline]
pub unsafe fn from_glib_ptr_borrow<'a>(ptr: *const *const $ffi_name) -> &'a Self {
&*(ptr as *const Self)
}
#[doc = "Borrows the underlying C value mutably."]
#[inline]
pub unsafe fn from_glib_ptr_borrow_mut<'a>(ptr: *mut *mut $ffi_name) -> &'a mut Self {
&mut *(ptr as *mut Self)
}
}
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::clone::Clone for $name $(<$($generic),+>)? {
#[inline]
fn clone(&self) -> Self {
Self {
inner: std::clone::Clone::clone(&self.inner),
}
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::GlibPtrDefault for $name $(<$($generic),+>)? {
type GlibType = *mut $ffi_name;
}
#[doc(hidden)]
unsafe impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::TransparentPtrType for $name $(<$($generic),+>)? {}
#[doc(hidden)]
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibPtr<'a, *const $ffi_name> for $name $(<$($generic),+>)? {
type Storage = std::marker::PhantomData<&'a $crate::boxed::Boxed<$ffi_name, Self>>;
#[inline]
fn to_glib_none(&'a self) -> $crate::translate::Stash<'a, *const $ffi_name, Self> {
let stash = $crate::translate::ToGlibPtr::to_glib_none(&self.inner);
$crate::translate::Stash(stash.0, stash.1)
}
#[inline]
fn to_glib_full(&self) -> *const $ffi_name {
$crate::translate::ToGlibPtr::to_glib_full(&self.inner)
}
}
#[doc(hidden)]
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibPtr<'a, *mut $ffi_name> for $name $(<$($generic),+>)? {
type Storage = std::marker::PhantomData<&'a $crate::boxed::Boxed<$ffi_name, Self>>;
#[inline]
fn to_glib_none(&'a self) -> $crate::translate::Stash<'a, *mut $ffi_name, Self> {
let stash = $crate::translate::ToGlibPtr::to_glib_none(&self.inner);
$crate::translate::Stash(stash.0 as *mut _, stash.1)
}
#[inline]
fn to_glib_full(&self) -> *mut $ffi_name {
$crate::translate::ToGlibPtr::to_glib_full(&self.inner) as *mut _
}
}
#[doc(hidden)]
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibPtrMut<'a, *mut $ffi_name> for $name $(<$($generic),+>)? {
type Storage = std::marker::PhantomData<&'a mut $crate::boxed::Boxed<$ffi_name, Self>>;
#[inline]
fn to_glib_none_mut(&'a mut self) -> $crate::translate::StashMut<'a, *mut $ffi_name, Self> {
let stash = $crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut self.inner);
$crate::translate::StashMut(stash.0, stash.1)
}
}
#[doc(hidden)]
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *mut *const $ffi_name> for $name $(<$($generic),+>)? {
type Storage = (std::marker::PhantomData<&'a [Self]>, Option<Vec<*const $ffi_name>>);
fn to_glib_none_from_slice(t: &'a [Self]) -> (*mut *const $ffi_name, Self::Storage) {
let mut v_ptr = Vec::with_capacity(t.len() + 1);
unsafe {
let ptr = v_ptr.as_mut_ptr();
std::ptr::copy_nonoverlapping(t.as_ptr() as *mut *const $ffi_name, ptr, t.len());
std::ptr::write(ptr.add(t.len()), std::ptr::null_mut());
v_ptr.set_len(t.len() + 1);
}
(v_ptr.as_ptr() as *mut *const $ffi_name, (std::marker::PhantomData, Some(v_ptr)))
}
fn to_glib_container_from_slice(t: &'a [Self]) -> (*mut *const $ffi_name, Self::Storage) {
let v_ptr = unsafe {
let v_ptr = $crate::ffi::g_malloc(std::mem::size_of::<*const $ffi_name>() * (t.len() + 1)) as *mut *const $ffi_name;
std::ptr::copy_nonoverlapping(t.as_ptr() as *mut *const $ffi_name, v_ptr, t.len());
std::ptr::write(v_ptr.add(t.len()), std::ptr::null_mut());
v_ptr
};
(v_ptr, (std::marker::PhantomData, None))
}
fn to_glib_full_from_slice(t: &[Self]) -> *mut *const $ffi_name {
unsafe {
let v_ptr = $crate::ffi::g_malloc(std::mem::size_of::<*const $ffi_name>() * (t.len() + 1)) as *mut *const $ffi_name;
for (i, s) in t.iter().enumerate() {
std::ptr::write(v_ptr.add(i), $crate::translate::ToGlibPtr::to_glib_full(s));
}
std::ptr::write(v_ptr.add(t.len()), std::ptr::null_mut());
v_ptr
}
}
}
#[doc(hidden)]
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *const *const $ffi_name> for $name $(<$($generic),+>)? {
type Storage = (std::marker::PhantomData<&'a [Self]>, Option<Vec<*const $ffi_name>>);
fn to_glib_none_from_slice(t: &'a [Self]) -> (*const *const $ffi_name, Self::Storage) {
let (ptr, stash) = $crate::translate::ToGlibContainerFromSlice::<'a, *mut *const $ffi_name>::to_glib_none_from_slice(t);
(ptr as *const *const $ffi_name, stash)
}
fn to_glib_container_from_slice(_: &'a [Self]) -> (*const *const $ffi_name, Self::Storage) {
// Can't have consumer free a *const pointer
unimplemented!()
}
fn to_glib_full_from_slice(_: &[Self]) -> *const *const $ffi_name {
// Can't have consumer free a *const pointer
unimplemented!()
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrNone<*mut $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_none(ptr: *mut $ffi_name) -> Self {
Self {
inner: $crate::translate::from_glib_none(ptr),
}
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrNone<*const $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_none(ptr: *const $ffi_name) -> Self {
Self {
inner: $crate::translate::from_glib_none(ptr),
}
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrFull<*mut $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_full(ptr: *mut $ffi_name) -> Self {
Self {
inner: $crate::translate::from_glib_full(ptr),
}
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrFull<*const $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_full(ptr: *const $ffi_name) -> Self {
Self {
inner: $crate::translate::from_glib_full(ptr),
}
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrBorrow<*mut $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_borrow(ptr: *mut $ffi_name) -> $crate::translate::Borrowed<Self> {
$crate::translate::Borrowed::new(
Self {
inner: $crate::translate::from_glib_borrow::<_, $crate::boxed::Boxed<_, _>>(ptr).into_inner(),
}
)
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrBorrow<*const $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_borrow(ptr: *const $ffi_name) -> $crate::translate::Borrowed<Self> {
$crate::translate::from_glib_borrow::<_, Self>(ptr as *mut $ffi_name)
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibContainerAsVec<*mut $ffi_name, *mut *mut $ffi_name> for $name $(<$($generic),+>)? {
unsafe fn from_glib_none_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
if num == 0 || ptr.is_null() {
return Vec::new();
}
let mut res = Vec::<Self>::with_capacity(num);
let res_ptr = res.as_mut_ptr();
for i in 0..num {
::std::ptr::write(res_ptr.add(i), $crate::translate::from_glib_none(std::ptr::read(ptr.add(i))));
}
res.set_len(num);
res
}
unsafe fn from_glib_container_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
let res = $crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, num);
$crate::ffi::g_free(ptr as *mut _);
res
}
unsafe fn from_glib_full_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
if num == 0 || ptr.is_null() {
$crate::ffi::g_free(ptr as *mut _);
return Vec::new();
}
let mut res = Vec::with_capacity(num);
let res_ptr = res.as_mut_ptr();
::std::ptr::copy_nonoverlapping(ptr as *mut Self, res_ptr, num);
res.set_len(num);
$crate::ffi::g_free(ptr as *mut _);
res
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrArrayContainerAsVec<*mut $ffi_name, *mut *mut $ffi_name> for $name $(<$($generic),+>)? {
unsafe fn from_glib_none_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
$crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
}
unsafe fn from_glib_container_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
$crate::translate::FromGlibContainerAsVec::from_glib_container_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
}
unsafe fn from_glib_full_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
$crate::translate::FromGlibContainerAsVec::from_glib_full_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::IntoGlibPtr<*mut $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn into_glib_ptr(self) -> *mut $ffi_name {
let s = std::mem::ManuallyDrop::new(self);
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_none(&*s).0 as *mut _
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::IntoGlibPtr<*const $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn into_glib_ptr(self) -> *const $ffi_name {
let s = std::mem::ManuallyDrop::new(self);
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_none(&*s).0 as *const _
}
}
};
(@value_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty) => {
};
(@value_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty, @type_ $get_type_expr:expr) => {
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::types::StaticType for $name $(<$($generic),+>)? {
#[inline]
fn static_type() -> $crate::types::Type {
#[allow(unused_unsafe)]
unsafe { $crate::translate::from_glib($get_type_expr) }
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ValueType for $name $(<$($generic),+>)? {
type Type = Self;
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ValueTypeOptional for $name $(<$($generic),+>)? { }
#[doc(hidden)]
unsafe impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::value::FromValue<'a> for $name $(<$($generic),+>)? {
type Checker = $crate::value::GenericValueTypeOrNoneChecker<Self>;
#[inline]
unsafe fn from_value(value: &'a $crate::Value) -> Self {
let ptr = $crate::gobject_ffi::g_value_dup_boxed($crate::translate::ToGlibPtr::to_glib_none(value).0);
debug_assert!(!ptr.is_null());
<Self as $crate::translate::FromGlibPtrFull<*mut $ffi_name>>::from_glib_full(ptr as *mut $ffi_name)
}
}
#[doc(hidden)]
unsafe impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::value::FromValue<'a> for &'a $name $(<$($generic),+>)? {
type Checker = $crate::value::GenericValueTypeOrNoneChecker<Self>;
#[inline]
unsafe fn from_value(value: &'a $crate::Value) -> Self {
debug_assert_eq!(std::mem::size_of::<Self>(), std::mem::size_of::<$crate::ffi::gpointer>());
let value = &*(value as *const $crate::Value as *const $crate::gobject_ffi::GValue);
debug_assert!(!value.data[0].v_pointer.is_null());
<$name $(<$($generic),+>)?>::from_glib_ptr_borrow(&value.data[0].v_pointer as *const $crate::ffi::gpointer as *const *const $ffi_name)
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ToValue for $name $(<$($generic),+>)? {
#[inline]
fn to_value(&self) -> $crate::Value {
unsafe {
let mut value = $crate::Value::from_type_unchecked(<Self as $crate::StaticType>::static_type());
$crate::gobject_ffi::g_value_take_boxed(
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_full(self) as *mut _,
);
value
}
}
#[inline]
fn value_type(&self) -> $crate::Type {
<Self as $crate::StaticType>::static_type()
}
}
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::convert::From<$name $(<$($generic),+>)?> for $crate::Value {
#[inline]
fn from(o: $name $(<$($generic),+>)?) -> Self {
unsafe {
let mut value = $crate::Value::from_type_unchecked(<$name $(<$($generic),+>)? as $crate::StaticType>::static_type());
$crate::gobject_ffi::g_value_take_boxed(
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
$crate::translate::IntoGlibPtr::<*mut $ffi_name>::into_glib_ptr(o) as *mut _,
);
value
}
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ToValueOptional for $name $(<$($generic),+>)? {
#[inline]
fn to_value_optional(s: Option<&Self>) -> $crate::Value {
let mut value = $crate::Value::for_value_type::<Self>();
unsafe {
$crate::gobject_ffi::g_value_take_boxed(
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_full(&s) as *mut _,
);
}
value
}
}
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::HasParamSpec for $name $(<$($generic),+>)? {
type ParamSpec = $crate::ParamSpecBoxed;
type SetValue = Self;
type BuilderFn = fn(&str) -> $crate::ParamSpecBoxedBuilder<Self>;
fn param_spec_builder() -> Self::BuilderFn {
|name| Self::ParamSpec::builder(name)
}
}
};
(@memory_manager_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty, @copy $copy_arg:ident $copy_expr:expr, @free $free_arg:ident $free_expr:expr) => {
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::boxed::BoxedMemoryManager<$ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn copy($copy_arg: *const $ffi_name) -> *mut $ffi_name {
$copy_expr
}
#[inline]
#[allow(clippy::no_effect)]
unsafe fn free($free_arg: *mut $ffi_name) {
$free_expr;
}
}
};
}
// The safety docs really belong in the wrapper!() macro for Boxed<T>
/// Memory management functions for a boxed type.
pub trait BoxedMemoryManager<T>: 'static {
/// Makes a copy.
unsafe fn copy(ptr: *const T) -> *mut T;
/// Frees the object.
unsafe fn free(ptr: *mut T);
}
/// Encapsulates memory management logic for boxed types.
#[repr(transparent)]
pub struct Boxed<T: 'static, MM: BoxedMemoryManager<T>> {
inner: ptr::NonNull<T>,
_dummy: PhantomData<*mut MM>,
}
impl<'a, T: 'static, MM: BoxedMemoryManager<T>> ToGlibPtr<'a, *const T> for Boxed<T, MM> {
type Storage = PhantomData<&'a Self>;
#[inline]
fn to_glib_none(&'a self) -> Stash<'a, *const T, Self> {
let ptr = self.inner.as_ptr();
Stash(ptr, PhantomData)
}
#[inline]
fn to_glib_full(&self) -> *const T {
let ptr = self.inner.as_ptr();
unsafe { MM::copy(ptr) }
}
}
impl<'a, T: 'static, MM: BoxedMemoryManager<T>> ToGlibPtrMut<'a, *mut T> for Boxed<T, MM> {
type Storage = PhantomData<&'a mut Self>;
#[inline]
fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut T, Self> {
let ptr = self.inner.as_ptr();
StashMut(ptr, PhantomData)
}
}
impl<T: 'static, MM: BoxedMemoryManager<T>> FromGlibPtrNone<*mut T> for Boxed<T, MM> {
#[inline]
unsafe fn from_glib_none(ptr: *mut T) -> Self {
debug_assert!(!ptr.is_null());
let ptr = MM::copy(ptr);
from_glib_full(ptr)
}
}
impl<T: 'static, MM: BoxedMemoryManager<T>> FromGlibPtrNone<*const T> for Boxed<T, MM> {
#[inline]
unsafe fn from_glib_none(ptr: *const T) -> Self {
debug_assert!(!ptr.is_null());
let ptr = MM::copy(ptr);
from_glib_full(ptr)
}
}
impl<T: 'static, MM: BoxedMemoryManager<T>> FromGlibPtrFull<*mut T> for Boxed<T, MM> {
#[inline]
unsafe fn from_glib_full(ptr: *mut T) -> Self {
debug_assert!(!ptr.is_null());
Self {
inner: ptr::NonNull::new_unchecked(ptr),
_dummy: PhantomData,
}
}
}
impl<T: 'static, MM: BoxedMemoryManager<T>> FromGlibPtrFull<*const T> for Boxed<T, MM> {
#[inline]
unsafe fn from_glib_full(ptr: *const T) -> Self {
debug_assert!(!ptr.is_null());
Self {
inner: ptr::NonNull::new_unchecked(ptr as *mut T),
_dummy: PhantomData,
}
}
}
impl<T: 'static, MM: BoxedMemoryManager<T>> FromGlibPtrBorrow<*mut T> for Boxed<T, MM> {
#[inline]
unsafe fn from_glib_borrow(ptr: *mut T) -> Borrowed<Self> {
debug_assert!(!ptr.is_null());
Borrowed::new(Self {
inner: ptr::NonNull::new_unchecked(ptr),
_dummy: PhantomData,
})
}
}
impl<T: 'static, MM: BoxedMemoryManager<T>> Drop for Boxed<T, MM> {
#[inline]
fn drop(&mut self) {
unsafe {
MM::free(self.inner.as_ptr());
}
}
}
impl<T: 'static, MM: BoxedMemoryManager<T>> fmt::Debug for Boxed<T, MM> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Boxed").field("inner", &self.inner).finish()
}
}
impl<T, MM: BoxedMemoryManager<T>> PartialOrd for Boxed<T, MM> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T, MM: BoxedMemoryManager<T>> Ord for Boxed<T, MM> {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.to_glib_none().0.cmp(&other.to_glib_none().0)
}
}
impl<T, MM: BoxedMemoryManager<T>> PartialEq for Boxed<T, MM> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.to_glib_none().0 == other.to_glib_none().0
}
}
impl<T, MM: BoxedMemoryManager<T>> Eq for Boxed<T, MM> {}
impl<T, MM: BoxedMemoryManager<T>> Hash for Boxed<T, MM> {
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.to_glib_none().0.hash(state)
}
}
impl<T: 'static, MM: BoxedMemoryManager<T>> Clone for Boxed<T, MM> {
#[inline]
fn clone(&self) -> Self {
unsafe { from_glib_none(self.to_glib_none().0 as *mut T) }
}
}
impl<T: 'static, MM: BoxedMemoryManager<T>> Deref for Boxed<T, MM> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe {
// This is safe because the pointer will remain valid while self is borrowed
&*self.to_glib_none().0
}
}
}
impl<T: 'static, MM: BoxedMemoryManager<T>> DerefMut for Boxed<T, MM> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe {
// This is safe because the pointer will remain valid while self is borrowed
&mut *self.to_glib_none_mut().0
}
}
}
@@ -0,0 +1,191 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{
any::Any,
cell::{Ref, RefMut},
};
use crate as glib;
use crate::{subclass::prelude::*, Object};
#[derive(thiserror::Error, Debug)]
pub enum BorrowError {
#[error("type of the inner value is not as requested")]
InvalidType,
#[error("value is already mutably borrowed")]
AlreadyBorrowed(#[from] std::cell::BorrowError),
}
#[derive(thiserror::Error, Debug)]
pub enum BorrowMutError {
#[error("type of the inner value is not as requested")]
InvalidType,
#[error("value is already immutably borrowed")]
AlreadyMutBorrowed(#[from] std::cell::BorrowMutError),
}
mod imp {
use std::{any::Any, cell::RefCell};
use crate as glib;
use crate::subclass::prelude::*;
#[derive(Debug)]
pub struct BoxedAnyObject {
pub value: RefCell<Box<dyn Any>>,
}
#[glib::object_subclass]
impl ObjectSubclass for BoxedAnyObject {
const NAME: &'static str = "BoxedAnyObject";
type Type = super::BoxedAnyObject;
}
impl Default for BoxedAnyObject {
fn default() -> Self {
Self {
value: RefCell::new(Box::new(None::<usize>)),
}
}
}
impl ObjectImpl for BoxedAnyObject {}
}
glib::wrapper! {
// rustdoc-stripper-ignore-next
/// This is a subclass of `glib::object::Object` capable of storing any Rust type.
/// It let's you insert a Rust type anywhere a `glib::object::Object` is needed.
/// The inserted value can then be borrowed as a Rust type, by using the various
/// provided methods.
///
/// # Examples
/// ```
/// use glib::prelude::*;
/// use glib::BoxedAnyObject;
/// use std::cell::Ref;
///
/// struct Author {
/// name: String,
/// subscribers: usize
/// }
/// // BoxedAnyObject can contain any custom type
/// let boxed = BoxedAnyObject::new(Author {
/// name: String::from("GLibAuthor"),
/// subscribers: 1000
/// });
///
/// // The value can be retrieved with `borrow`
/// let author: Ref<Author> = boxed.borrow();
/// ```
///
/// ```ignore
/// use gio::ListStore;
///
/// // The boxed data can be stored as a `glib::object::Object`
/// let list = ListStore::new::<BoxedAnyObject>();
/// list.append(&boxed);
/// ```
pub struct BoxedAnyObject(ObjectSubclass<imp::BoxedAnyObject>);
}
impl BoxedAnyObject {
// rustdoc-stripper-ignore-next
/// Creates a new `BoxedAnyObject` containing `value`
pub fn new<T: 'static>(value: T) -> Self {
let obj: Self = Object::new();
obj.replace(value);
obj
}
// rustdoc-stripper-ignore-next
/// Replaces the wrapped value with a new one, returning the old value, without deinitializing either one.
/// The returned value is inside a `Box` and must be manually downcasted if needed.
pub fn replace<T: 'static>(&self, t: T) -> Box<dyn Any> {
self.imp().value.replace(Box::new(t) as Box<dyn Any>)
}
// rustdoc-stripper-ignore-next
/// Immutably borrows the wrapped value, returning an error if the value is currently mutably
/// borrowed or if it's not of type `T`.
///
/// The borrow lasts until the returned `Ref` exits scope. Multiple immutable borrows can be
/// taken out at the same time.
///
/// This is the non-panicking variant of [`borrow`](#method.borrow).
pub fn try_borrow<T: 'static>(&self) -> Result<Ref<'_, T>, BorrowError> {
// The required function is only available on nightly:
// https://doc.rust-lang.org/std/cell/struct.Ref.html#method.filter_map.
// As a workaround, I check if everything is safe, then I unwrap
let borrowed = self.imp().value.try_borrow()?;
borrowed
.as_ref()
.downcast_ref::<T>()
.ok_or(BorrowError::InvalidType)?;
Ok(self.borrow()) // Now this won't panic
}
// rustdoc-stripper-ignore-next
/// Mutably borrows the wrapped value, returning an error if the value is currently borrowed.
/// or if it's not of type `T`.
///
/// The borrow lasts until the returned `RefMut` or all `RefMut`s derived
/// from it exit scope. The value cannot be borrowed while this borrow is
/// active.
///
/// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).
pub fn try_borrow_mut<T: 'static>(&mut self) -> Result<RefMut<'_, T>, BorrowMutError> {
// The required function is only available on nightly:
// https://doc.rust-lang.org/std/cell/struct.Ref.html#method.filter_map
// As a workaround, I check if everything is safe, then I unwrap.
let mut borrowed_mut = self.imp().value.try_borrow_mut()?;
borrowed_mut
.as_mut()
.downcast_mut::<T>()
.ok_or(BorrowMutError::InvalidType)?;
drop(borrowed_mut);
Ok(self.borrow_mut()) // Now this won't panic
}
// rustdoc-stripper-ignore-next
/// Immutably borrows the wrapped value.
///
/// The borrow lasts until the returned `Ref` exits scope. Multiple
/// immutable borrows can be taken out at the same time.
///
/// # Panics
///
/// Panics if the value is currently mutably borrowed or if it's not of type `T`.
///
/// For a non-panicking variant, use
/// [`try_borrow`](#method.try_borrow).
pub fn borrow<T: 'static>(&self) -> Ref<'_, T> {
Ref::map(self.imp().value.borrow(), |value| {
value
.as_ref()
.downcast_ref::<T>()
.expect("can't downcast value to requested type")
})
}
// rustdoc-stripper-ignore-next
/// Mutably borrows the wrapped value.
///
/// The borrow lasts until the returned `RefMut` or all `RefMut`s derived
/// from it exit scope. The value cannot be borrowed while this borrow is
/// active.
///
/// # Panics
///
/// Panics if the value is currently borrowed or if it's not of type `T`.
///
/// For a non-panicking variant, use
/// [`try_borrow_mut`](#method.try_borrow_mut).
pub fn borrow_mut<T: 'static>(&self) -> RefMut<'_, T> {
RefMut::map(self.imp().value.borrow_mut(), |value| {
value
.as_mut()
.downcast_mut::<T>()
.expect("can't downcast value to requested type")
})
}
}
+643
View File
@@ -0,0 +1,643 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! `IMPL` BoxedInline wrapper implementation.
// rustdoc-stripper-ignore-next
/// Wrapper implementations for BoxedInline types. See `wrapper!`.
#[macro_export]
macro_rules! glib_boxed_inline_wrapper {
([$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty
$(, @type_ $get_type_expr:expr)?) => {
$(#[$attr])*
#[repr(transparent)]
$visibility struct $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? {
pub(crate) inner: $ffi_name,
$(pub(crate) phantom: std::marker::PhantomData<$($generic),+>,)?
}
#[allow(clippy::incorrect_clone_impl_on_copy_type)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::clone::Clone for $name $(<$($generic),+>)? {
#[inline]
fn clone(&self) -> Self {
Self {
inner: std::clone::Clone::clone(&self.inner),
$(phantom: std::marker::PhantomData::<$($generic),+>)?
}
}
}
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::marker::Copy for $name $(<$($generic),+>)? {}
$crate::glib_boxed_inline_wrapper!(
@generic_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name,
@copy ptr unsafe { let copy = $crate::ffi::g_malloc(std::mem::size_of::<$ffi_name>()) as *mut $ffi_name; std::ptr::copy_nonoverlapping(ptr, copy, 1); copy },
@free ptr unsafe { $crate::ffi::g_free(ptr as *mut _); },
@init _ptr (), @copy_into dest src std::ptr::copy_nonoverlapping(src, dest, 1), @clear _ptr ()
);
$crate::glib_boxed_inline_wrapper!(@value_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name $(, @type_ $get_type_expr)?);
};
([$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty,
@copy $copy_arg:ident $copy_expr:expr, @free $free_arg:ident $free_expr:expr
$(, @type_ $get_type_expr:expr)?) => {
$(#[$attr])*
#[repr(transparent)]
$visibility struct $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? {
pub(crate) inner: $ffi_name,
$(pub(crate) phantom: std::marker::PhantomData<$($generic),+>,)?
}
#[allow(clippy::incorrect_clone_impl_on_copy_type)]
#[allow(clippy::non_canonical_clone_impl)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::clone::Clone for $name $(<$($generic),+>)? {
#[inline]
fn clone(&self) -> Self {
Self {
inner: std::clone::Clone::clone(&self.inner),
$(phantom: std::marker::PhantomData::<$($generic),+>)?
}
}
}
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::marker::Copy for $name $(<$($generic),+>)? {}
$crate::glib_boxed_inline_wrapper!(
@generic_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name,
@copy $copy_arg $copy_expr, @free $free_arg $free_expr,
@init _ptr (), @copy_into dest src std::ptr::copy_nonoverlapping(src, dest, 1), @clear _ptr ()
);
$crate::glib_boxed_inline_wrapper!(@value_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name $(, @type_ $get_type_expr)?);
};
([$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty,
@init $init_arg:ident $init_expr:expr, @copy_into $copy_into_arg_dest:ident $copy_into_arg_src:ident $copy_into_expr:expr, @clear $clear_arg:ident $clear_expr:expr
$(, @type_ $get_type_expr:expr)?) => {
$(#[$attr])*
#[repr(transparent)]
$visibility struct $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? {
pub(crate) inner: $ffi_name,
$(pub(crate) phantom: std::marker::PhantomData<$($generic),+>,)?
}
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::clone::Clone for $name $(<$($generic),+>)? {
#[inline]
fn clone(&self) -> Self {
unsafe {
$crate::translate::from_glib_none(&self.inner as *const $ffi_name)
}
}
}
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? Drop for $name $(<$($generic),+>)? {
#[inline]
fn drop(&mut self) {
unsafe {
let clear = |$clear_arg: *mut $ffi_name| $clear_expr;
clear(&mut self.inner as *mut $ffi_name);
}
}
}
$crate::glib_boxed_inline_wrapper!(
@generic_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name,
@copy ptr unsafe { let copy = $crate::ffi::g_malloc(std::mem::size_of::<$ffi_name>()) as *mut $ffi_name; let c = |$copy_into_arg_dest, $copy_into_arg_src| $copy_into_expr; c(copy, ptr); copy },
@free ptr unsafe { let c = |$clear_arg| $clear_expr; c(ptr); $crate::ffi::g_free(ptr as *mut _); },
@init $init_arg $init_expr, @copy_into $copy_into_arg_dest $copy_into_arg_src $copy_into_expr, @clear $clear_arg $clear_expr
);
$crate::glib_boxed_inline_wrapper!(@value_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name $(, @type_ $get_type_expr)?);
};
([$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty,
@copy $copy_arg:ident $copy_expr:expr, @free $free_arg:ident $free_expr:expr,
@init $init_arg:ident $init_expr:expr, @copy_into $copy_into_arg_dest:ident $copy_into_arg_src:ident $copy_into_expr:expr, @clear $clear_arg:ident $clear_expr:expr
$(, @type_ $get_type_expr:expr)?) => {
$(#[$attr])*
#[repr(transparent)]
$visibility struct $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? {
pub(crate) inner: $ffi_name,
$(pub(crate) phantom: std::marker::PhantomData<$($generic),+>,)?
}
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::clone::Clone for $name $(<$($generic),+>)? {
#[inline]
fn clone(&self) -> Self {
unsafe {
$crate::translate::from_glib_none(&self.inner as *const $ffi_name)
}
}
}
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? Drop for $name $(<$($generic),+>)? {
#[inline]
fn drop(&mut self) {
unsafe {
let clear = |$clear_arg: *mut $ffi_name| $clear_expr;
clear(&mut self.inner as *mut $ffi_name);
}
}
}
$crate::glib_boxed_inline_wrapper!(
@generic_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name,
@copy $copy_arg $copy_expr, @free $free_arg $free_expr,
@init $init_arg $init_expr, @copy_into $copy_into_arg_dest $copy_into_arg_src $copy_into_expr, @clear $clear_arg $clear_expr
);
$crate::glib_boxed_inline_wrapper!(@value_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name $(, @type_ $get_type_expr)?);
};
(@generic_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty,
@copy $copy_arg:ident $copy_expr:expr, @free $free_arg:ident $free_expr:expr,
@init $init_arg:ident $init_expr:expr, @copy_into $copy_into_arg_dest:ident $copy_into_arg_src:ident $copy_into_expr:expr, @clear $clear_arg:ident $clear_expr:expr) => {
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $name $(<$($generic),+>)? {
#[inline]
pub fn as_ptr(&self) -> *mut $ffi_name {
&self.inner as *const $ffi_name as *mut _
}
#[doc = "Borrows the underlying C value."]
#[inline]
pub unsafe fn from_glib_ptr_borrow<'a>(ptr: *const $ffi_name) -> &'a Self {
&*(ptr as *const Self)
}
#[doc = "Borrows the underlying C value mutably."]
#[inline]
pub unsafe fn from_glib_ptr_borrow_mut<'a>(ptr: *mut $ffi_name) -> &'a mut Self {
&mut *(ptr as *mut Self)
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::GlibPtrDefault for $name $(<$($generic),+>)? {
type GlibType = *mut $ffi_name;
}
#[doc(hidden)]
unsafe impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::TransparentType for $name $(<$($generic),+>)? {
type GlibType = $ffi_name;
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::Uninitialized for $name $(<$($generic),+>)? {
#[inline]
unsafe fn uninitialized() -> Self {
let mut v = std::mem::MaybeUninit::zeroed();
let init = |$init_arg: *mut $ffi_name| $init_expr;
init(v.as_mut_ptr());
Self {
inner: v.assume_init(),
$(phantom: std::marker::PhantomData::<$($generic),+>)?
}
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::UnsafeFrom<$ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn unsafe_from(t: $ffi_name) -> Self {
Self {
inner: t,
$(phantom: std::marker::PhantomData::<$($generic),+>)?
}
}
}
#[doc(hidden)]
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibPtr<'a, *const $ffi_name> for $name $(<$($generic),+>)? {
type Storage = std::marker::PhantomData<&'a Self>;
#[inline]
fn to_glib_none(&'a self) -> $crate::translate::Stash<'a, *const $ffi_name, Self> {
$crate::translate::Stash(&self.inner as *const $ffi_name, std::marker::PhantomData)
}
#[inline]
fn to_glib_full(&self) -> *const $ffi_name {
unsafe {
let copy = |$copy_arg: *const $ffi_name| $copy_expr;
copy(&self.inner as *const $ffi_name)
}
}
}
#[doc(hidden)]
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibPtrMut<'a, *mut $ffi_name> for $name $(<$($generic),+>)? {
type Storage = std::marker::PhantomData<&'a mut Self>;
#[inline]
fn to_glib_none_mut(&'a mut self) -> $crate::translate::StashMut<'a, *mut $ffi_name, Self> {
let ptr = &mut self.inner as *mut $ffi_name;
$crate::translate::StashMut(ptr, std::marker::PhantomData)
}
}
#[doc(hidden)]
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *mut *const $ffi_name> for $name $(<$($generic),+>)? {
type Storage = (std::marker::PhantomData<&'a [Self]>, Option<Vec<*const $ffi_name>>);
fn to_glib_none_from_slice(t: &'a [Self]) -> (*mut *const $ffi_name, Self::Storage) {
let mut v: Vec<_> = t.iter().map(|s| &s.inner as *const $ffi_name).collect();
v.push(std::ptr::null_mut() as *const $ffi_name);
(v.as_mut_ptr(), (std::marker::PhantomData, Some(v)))
}
fn to_glib_container_from_slice(t: &'a [Self]) -> (*mut *const $ffi_name, Self::Storage) {
let v_ptr = unsafe {
let v_ptr = $crate::ffi::g_malloc(std::mem::size_of::<*const $ffi_name>() * (t.len() + 1)) as *mut *const $ffi_name;
for (i, s) in t.iter().enumerate() {
std::ptr::write(v_ptr.add(i), &s.inner as *const $ffi_name);
}
std::ptr::write(v_ptr.add(t.len()), std::ptr::null_mut());
v_ptr
};
(v_ptr, (std::marker::PhantomData, None))
}
fn to_glib_full_from_slice(t: &[Self]) -> *mut *const $ffi_name {
unsafe {
let v_ptr = $crate::ffi::g_malloc(std::mem::size_of::<*const $ffi_name>() * (t.len() + 1)) as *mut *const $ffi_name;
for (i, s) in t.iter().enumerate() {
std::ptr::write(v_ptr.add(i), $crate::translate::ToGlibPtr::to_glib_full(s));
}
std::ptr::write(v_ptr.add(t.len()), std::ptr::null_mut());
v_ptr
}
}
}
#[doc(hidden)]
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *const *const $ffi_name> for $name $(<$($generic),+>)? {
type Storage = (std::marker::PhantomData<&'a [Self]>, Option<Vec<*const $ffi_name>>);
#[inline]
fn to_glib_none_from_slice(t: &'a [Self]) -> (*const *const $ffi_name, Self::Storage) {
let (ptr, stash) = $crate::translate::ToGlibContainerFromSlice::<'a, *mut *const $ffi_name>::to_glib_none_from_slice(t);
(ptr as *const *const $ffi_name, stash)
}
fn to_glib_container_from_slice(_: &'a [Self]) -> (*const *const $ffi_name, Self::Storage) {
// Can't have consumer free a *const pointer
unimplemented!()
}
fn to_glib_full_from_slice(_: &[Self]) -> *const *const $ffi_name {
// Can't have consumer free a *const pointer
unimplemented!()
}
}
#[doc(hidden)]
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *mut $ffi_name> for $name $(<$($generic),+>)? {
type Storage = std::marker::PhantomData<&'a [Self]>;
#[inline]
fn to_glib_none_from_slice(t: &'a [Self]) -> (*mut $ffi_name, Self::Storage) {
(t.as_ptr() as *mut $ffi_name, std::marker::PhantomData)
}
fn to_glib_container_from_slice(t: &'a [Self]) -> (*mut $ffi_name, Self::Storage) {
(
$crate::translate::ToGlibContainerFromSlice::<'a, *mut $ffi_name>::to_glib_full_from_slice(t),
std::marker::PhantomData,
)
}
fn to_glib_full_from_slice(t: &[Self]) -> *mut $ffi_name {
let v_ptr = unsafe {
let v_ptr = $crate::ffi::g_malloc(std::mem::size_of::<$ffi_name>()) as *mut $ffi_name;
for (i, s) in t.iter().enumerate() {
let copy_into = |$copy_into_arg_dest: *mut $ffi_name, $copy_into_arg_src: *const $ffi_name| $copy_into_expr;
copy_into(v_ptr.add(i), &s.inner as *const $ffi_name);
}
v_ptr
};
v_ptr
}
}
#[doc(hidden)]
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *const $ffi_name> for $name $(<$($generic),+>)? {
type Storage = std::marker::PhantomData<&'a [Self]>;
#[inline]
fn to_glib_none_from_slice(t: &'a [Self]) -> (*const $ffi_name, Self::Storage) {
let (ptr, stash) = $crate::translate::ToGlibContainerFromSlice::<'a, *mut $ffi_name>::to_glib_none_from_slice(t);
(ptr as *const $ffi_name, stash)
}
fn to_glib_container_from_slice(_: &'a [Self]) -> (*const $ffi_name, Self::Storage) {
// Can't have consumer free a *const pointer
unimplemented!()
}
fn to_glib_full_from_slice(_: &[Self]) -> *const $ffi_name {
// Can't have consumer free a *const pointer
unimplemented!()
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrNone<*mut $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_none(ptr: *mut $ffi_name) -> Self {
debug_assert!(!ptr.is_null());
let mut v = <Self as $crate::translate::Uninitialized>::uninitialized();
let copy_into = |$copy_into_arg_dest: *mut $ffi_name, $copy_into_arg_src: *const $ffi_name| $copy_into_expr;
copy_into(&mut v.inner as *mut $ffi_name, ptr as *const $ffi_name);
v
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrNone<*const $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_none(ptr: *const $ffi_name) -> Self {
$crate::translate::from_glib_none::<_, Self>(ptr as *mut $ffi_name)
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrFull<*mut $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_full(ptr: *mut $ffi_name) -> Self {
debug_assert!(!ptr.is_null());
let mut v = <Self as $crate::translate::Uninitialized>::uninitialized();
let copy_into = |$copy_into_arg_dest: *mut $ffi_name, $copy_into_arg_src: *const $ffi_name| $copy_into_expr;
copy_into(&mut v.inner as *mut $ffi_name, ptr as *const $ffi_name);
let free = |$free_arg: *mut $ffi_name| $free_expr;
free(ptr);
v
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrFull<*const $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_full(ptr: *const $ffi_name) -> Self {
$crate::translate::from_glib_full::<_, Self>(ptr as *mut $ffi_name)
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrBorrow<*mut $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_borrow(ptr: *mut $ffi_name) -> $crate::translate::Borrowed<Self> {
debug_assert!(!ptr.is_null());
$crate::translate::Borrowed::new(Self {
inner: std::ptr::read(ptr),
$(phantom: std::marker::PhantomData::<$($generic),+>)?
})
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrBorrow<*const $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_borrow(ptr: *const $ffi_name) -> $crate::translate::Borrowed<Self> {
$crate::translate::from_glib_borrow::<_, Self>(ptr as *mut $ffi_name)
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibContainerAsVec<*mut $ffi_name, *mut $ffi_name> for $name $(<$($generic),+>)? {
unsafe fn from_glib_none_num_as_vec(ptr: *mut $ffi_name, num: usize) -> Vec<Self> {
$crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr as *const _, num)
}
unsafe fn from_glib_container_num_as_vec(ptr: *mut $ffi_name, num: usize) -> Vec<Self> {
$crate::translate::FromGlibContainerAsVec::from_glib_container_num_as_vec(ptr as *const _, num)
}
unsafe fn from_glib_full_num_as_vec(ptr: *mut $ffi_name, num: usize) -> Vec<Self> {
$crate::translate::FromGlibContainerAsVec::from_glib_full_num_as_vec(ptr as *const _, num)
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibContainerAsVec<*mut $ffi_name, *const $ffi_name> for $name $(<$($generic),+>)? {
unsafe fn from_glib_none_num_as_vec(ptr: *const $ffi_name, num: usize) -> Vec<Self> {
if num == 0 || ptr.is_null() {
return Vec::new();
}
let mut res = Vec::<Self>::with_capacity(num);
let res_ptr = res.as_mut_ptr();
for i in 0..num {
::std::ptr::write(res_ptr.add(i), $crate::translate::from_glib_none(ptr.add(i)));
}
res.set_len(num);
res
}
unsafe fn from_glib_container_num_as_vec(ptr: *const $ffi_name, num: usize) -> Vec<Self> {
let res = $crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, num);
$crate::ffi::g_free(ptr as *mut _);
res
}
unsafe fn from_glib_full_num_as_vec(ptr: *const $ffi_name, num: usize) -> Vec<Self> {
if num == 0 || ptr.is_null() {
$crate::ffi::g_free(ptr as *mut _);
return Vec::new();
}
let mut res = Vec::with_capacity(num);
let res_ptr = res.as_mut_ptr();
::std::ptr::copy_nonoverlapping(ptr as *mut Self, res_ptr, num);
res.set_len(num);
$crate::ffi::g_free(ptr as *mut _);
res
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibContainerAsVec<*mut $ffi_name, *mut *mut $ffi_name> for $name $(<$($generic),+>)? {
unsafe fn from_glib_none_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
if num == 0 || ptr.is_null() {
return Vec::new();
}
let mut res = Vec::<Self>::with_capacity(num);
let res_ptr = res.as_mut_ptr();
for i in 0..num {
::std::ptr::write(res_ptr.add(i), $crate::translate::from_glib_none(::std::ptr::read(ptr.add(i))));
}
res.set_len(num);
res
}
unsafe fn from_glib_container_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
let res = $crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, num);
$crate::ffi::g_free(ptr as *mut _);
res
}
unsafe fn from_glib_full_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
if num == 0 || ptr.is_null() {
$crate::ffi::g_free(ptr as *mut _);
return Vec::new();
}
let mut res = Vec::<Self>::with_capacity(num);
let res_ptr = res.as_mut_ptr();
for i in 0..num {
::std::ptr::write(res_ptr.add(i), $crate::translate::from_glib_full(::std::ptr::read(ptr.add(i))));
}
res.set_len(num);
$crate::ffi::g_free(ptr as *mut _);
res
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrArrayContainerAsVec<*mut $ffi_name, *mut *mut $ffi_name> for $name $(<$($generic),+>)? {
unsafe fn from_glib_none_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
$crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
}
unsafe fn from_glib_container_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
$crate::translate::FromGlibContainerAsVec::from_glib_container_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
}
unsafe fn from_glib_full_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
$crate::translate::FromGlibContainerAsVec::from_glib_full_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::IntoGlibPtr<*mut $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn into_glib_ptr(self) -> *mut $ffi_name {
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_full(&self) as *mut _
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::IntoGlibPtr<*const $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn into_glib_ptr(self) -> *const $ffi_name {
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_full(&self)
}
}
};
(@value_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty) => { };
(@value_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty, @type_ $get_type_expr:expr) => {
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::types::StaticType for $name $(<$($generic),+>)? {
#[inline]
fn static_type() -> $crate::types::Type {
#[allow(unused_unsafe)]
unsafe { $crate::translate::from_glib($get_type_expr) }
}
}
#[doc(hidden)]
impl$(<$($generic: 'static + $($bound $(+ $bound2)*)?),+>)? $crate::value::ValueType for $name $(<$($generic),+>)? {
type Type = Self;
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ValueTypeOptional for $name $(<$($generic),+>)? { }
#[doc(hidden)]
unsafe impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::FromValue<'_> for $name $(<$($generic),+>)? {
type Checker = $crate::value::GenericValueTypeOrNoneChecker<Self>;
#[inline]
unsafe fn from_value(value: &'_ $crate::Value) -> Self {
let ptr = $crate::gobject_ffi::g_value_get_boxed($crate::translate::ToGlibPtr::to_glib_none(value).0);
debug_assert!(!ptr.is_null());
<Self as $crate::translate::FromGlibPtrNone<*const $ffi_name>>::from_glib_none(ptr as *const $ffi_name)
}
}
#[doc(hidden)]
unsafe impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::FromValue<'_> for &'_ $name $(<$($generic),+>)? {
type Checker = $crate::value::GenericValueTypeOrNoneChecker<Self>;
#[inline]
unsafe fn from_value(value: &'_ $crate::Value) -> Self {
let ptr = $crate::gobject_ffi::g_value_get_boxed($crate::translate::ToGlibPtr::to_glib_none(value).0);
debug_assert!(!ptr.is_null());
&*(ptr as *const $ffi_name as *const $name $(<$($generic),+>)?)
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ToValue for $name $(<$($generic),+>)? {
#[inline]
fn to_value(&self) -> $crate::Value {
unsafe {
let mut value = $crate::Value::from_type_unchecked(<Self as $crate::StaticType>::static_type());
$crate::gobject_ffi::g_value_set_boxed(
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_none(self).0 as *mut _,
);
value
}
}
#[inline]
fn value_type(&self) -> $crate::Type {
<Self as $crate::StaticType>::static_type()
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? ::std::convert::From<$name $(<$($generic),+>)?> for $crate::Value {
#[inline]
fn from(v: $name $(<$($generic),+>)?) -> Self {
$crate::value::ToValue::to_value(&v)
}
}
#[doc(hidden)]
impl $(<$($generic: 'static + $($bound $(+ $bound2)*)?),+>)? $crate::value::ToValueOptional for $name $(<$($generic),+>)? {
#[inline]
fn to_value_optional(s: Option<&Self>) -> $crate::Value {
let mut value = $crate::Value::for_value_type::<Self>();
unsafe {
$crate::gobject_ffi::g_value_set_boxed(
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_none(&s).0 as *mut _,
);
}
value
}
}
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::HasParamSpec for $name $(<$($generic),+>)? {
type ParamSpec = $crate::ParamSpecBoxed;
type SetValue = Self;
type BuilderFn = fn(&str) -> $crate::ParamSpecBoxedBuilder<Self>;
fn param_spec_builder() -> Self::BuilderFn {
|name| Self::ParamSpec::builder(name)
}
}
};
}
@@ -0,0 +1,385 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use crate::{log as glib_log, translate::*};
// rustdoc-stripper-ignore-next
/// Enumeration of the possible formatting behaviours for a
/// [`GlibLogger`](struct.GlibLogger.html).
///
/// In order to use this type, `glib` must be built with the `log` feature
/// enabled.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GlibLoggerFormat {
// rustdoc-stripper-ignore-next
/// A simple format, writing only the message on output.
Plain,
// rustdoc-stripper-ignore-next
/// A simple format, writing file, line and message on output.
LineAndFile,
// rustdoc-stripper-ignore-next
/// A logger using glib structured logging.
Structured,
}
// rustdoc-stripper-ignore-next
/// Enumeration of the possible domain handling behaviours for a
/// [`GlibLogger`](struct.GlibLogger.html).
///
/// In order to use this type, `glib` must be built with the `log` feature
/// enabled.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GlibLoggerDomain {
// rustdoc-stripper-ignore-next
/// Logs will have no domain specified.
None,
// rustdoc-stripper-ignore-next
/// Logs will use the `target` of the log crate as a domain; this allows
/// Rust code, like `warn!(target: "my-domain", "...");` to log to the glib
/// logger using the specified domain.
CrateTarget,
// rustdoc-stripper-ignore-next
/// Logs will use the crate path as the log domain.
CratePath,
}
// rustdoc-stripper-ignore-next
/// An implementation of a [`log`](https://crates.io/crates/log) compatible
/// logger which logs over glib logging facilities.
///
/// In order to use this type, `glib` must be built with the `log` feature
/// enabled.
///
/// Use this if you want to use glib as the main logging output in your application,
/// and want to route all logging happening through the log crate to glib logging.
/// If you want the opposite, see
/// [`rust_log_handler`](fn.rust_log_handler.html).
///
/// NOTE: This should never be used when
/// [`rust_log_handler`](fn.rust_log_handler.html) has
/// been registered as a default glib log handler, otherwise a stack overflow
/// will occur.
///
/// Example:
///
/// ```no_compile
/// static glib_logger: glib::GlibLogger = glib::GlibLogger::new(
/// glib::GlibLoggerFormat::Plain,
/// glib::GlibLoggerDomain::CrateTarget,
/// );
///
/// log::set_logger(&glib_logger);
/// log::set_max_level(log::LevelFilter::Debug);
///
/// log::info!("This line will get logged by glib");
/// ```
#[derive(Debug)]
pub struct GlibLogger {
format: GlibLoggerFormat,
domain: GlibLoggerDomain,
}
impl GlibLogger {
// rustdoc-stripper-ignore-next
/// Creates a new instance of [`GlibLogger`](struct.GlibLogger.html).
/// See documentation of [`GlibLogger`](struct.GlibLogger.html) for more
/// information.
///
/// Example:
///
/// ```no_compile
/// static glib_logger: glib::GlibLogger = glib::GlibLogger::new(
/// glib::GlibLoggerFormat::Plain,
/// glib::GlibLoggerDomain::CrateTarget,
/// );
///
/// log::set_logger(&glib_logger);
/// log::set_max_level(log::LevelFilter::Debug);
///
/// log::info!("This line will get logged by glib");
/// ```
pub const fn new(format: GlibLoggerFormat, domain: GlibLoggerDomain) -> Self {
Self { format, domain }
}
fn level_to_glib(level: rs_log::Level) -> crate::LogLevel {
match level {
// Errors are mapped to critical to avoid automatic termination
rs_log::Level::Error => crate::LogLevel::Critical,
rs_log::Level::Warn => crate::LogLevel::Warning,
rs_log::Level::Info => crate::LogLevel::Info,
rs_log::Level::Debug => crate::LogLevel::Debug,
// There is no equivalent to trace level in glib
rs_log::Level::Trace => crate::LogLevel::Debug,
}
}
#[doc(alias = "g_log")]
fn write_log(domain: Option<&str>, level: rs_log::Level, message: &std::fmt::Arguments<'_>) {
unsafe {
use std::fmt::Write;
let mut message_builder = crate::GStringBuilder::default();
if write!(&mut message_builder, "{}", message).is_err() {
return;
}
let message = message_builder.into_string();
crate::ffi::g_log(
domain.to_glib_none().0,
GlibLogger::level_to_glib(level).into_glib(),
b"%s\0".as_ptr() as *const _,
ToGlibPtr::<*const std::os::raw::c_char>::to_glib_none(&message).0,
);
}
}
fn write_log_structured(
domain: Option<&str>,
level: rs_log::Level,
file: Option<&str>,
line: Option<u32>,
func: Option<&str>,
message: &str,
) {
let line = line.map(|l| l.to_string());
let line = line.as_deref();
crate::log_structured!(
domain.unwrap_or("default"),
GlibLogger::level_to_glib(level),
{
"CODE_FILE" => file.unwrap_or("<unknown file>");
"CODE_LINE" => line.unwrap_or("<unknown line>");
"CODE_FUNC" => func.unwrap_or("<unknown module path>");
"MESSAGE" => message;
}
);
}
}
impl rs_log::Log for GlibLogger {
fn enabled(&self, _: &rs_log::Metadata) -> bool {
true
}
fn log(&self, record: &rs_log::Record) {
if !self.enabled(record.metadata()) {
return;
}
let domain = match &self.domain {
GlibLoggerDomain::None => None,
GlibLoggerDomain::CrateTarget => Some(record.metadata().target()),
GlibLoggerDomain::CratePath => record.module_path(),
};
match self.format {
GlibLoggerFormat::Plain => {
GlibLogger::write_log(domain, record.level(), record.args());
}
GlibLoggerFormat::LineAndFile => {
match (record.file(), record.line()) {
(Some(file), Some(line)) => {
GlibLogger::write_log(
domain,
record.level(),
&format_args!("{}:{}: {}", file, line, record.args()),
);
}
(Some(file), None) => {
GlibLogger::write_log(
domain,
record.level(),
&format_args!("{}: {}", file, record.args()),
);
}
_ => {
GlibLogger::write_log(domain, record.level(), record.args());
}
};
}
GlibLoggerFormat::Structured => {
let args = record.args();
if let Some(s) = args.as_str() {
GlibLogger::write_log_structured(
domain,
record.level(),
record.file(),
record.line(),
record.module_path(),
s,
);
} else {
GlibLogger::write_log_structured(
domain,
record.level(),
record.file(),
record.line(),
record.module_path(),
&args.to_string(),
);
}
}
};
}
fn flush(&self) {}
}
// rustdoc-stripper-ignore-next
/// Provides a glib log handler which routes all logging messages to the
/// [`log crate`](https://crates.io/crates/log).
///
/// In order to use this function, `glib` must be built with the `log` feature
/// enabled.
///
/// Use this function if you want to use the log crate as the main logging
/// output in your application, and want to route all logging happening in
/// glib to the log crate. If you want the opposite, use [`GlibLogger`](struct.GlibLogger.html).
///
/// NOTE: This should never be used when [`GlibLogger`](struct.GlibLogger.html) is
/// registered as a logger, otherwise a stack overflow will occur.
///
/// ```no_run
/// glib::log_set_default_handler(glib::rust_log_handler);
/// ```
pub fn rust_log_handler(domain: Option<&str>, level: glib_log::LogLevel, message: &str) {
let level = match level {
glib_log::LogLevel::Error | glib_log::LogLevel::Critical => rs_log::Level::Error,
glib_log::LogLevel::Warning => rs_log::Level::Warn,
glib_log::LogLevel::Message | glib_log::LogLevel::Info => rs_log::Level::Info,
glib_log::LogLevel::Debug => rs_log::Level::Debug,
};
rs_log::log!(target: domain.unwrap_or("<null>"), level, "{}", message);
}
// rustdoc-stripper-ignore-next
/// A macro which behaves exactly as `log::error!` except that it sets the
/// current log target to the contents of a `G_LOG_DOMAIN` constant (and fails
/// to build if not defined).
///
/// In order to use this macro, `glib` must be built with the `log_macros`
/// feature enabled and the [`GlibLogger`](struct.GlibLogger.html) must have been
/// initialized using [`GlibLoggerDomain::CrateTarget`](enum.GlibLoggerDomain.html).
///
/// ```no_run
/// static G_LOG_DOMAIN: &str = "my-domain";
///
/// glib::error!("This will be logged under 'my-domain'");
/// ```
#[macro_export]
#[cfg(any(docsrs, feature = "log_macros"))]
#[cfg_attr(docsrs, doc(cfg(feature = "log_macros")))]
macro_rules! error {
(target: $target:expr, $($arg:tt)+) => (
$crate::rs_log::log!(target: $target, $crate::rs_log::Level::Error, $($arg)+);
);
($($arg:tt)+) => (
$crate::rs_log::log!(target: G_LOG_DOMAIN, $crate::rs_log::Level::Error, $($arg)+);
)
}
// rustdoc-stripper-ignore-next
/// A macro which behaves exactly as `log::warn!` except that it sets the
/// current log target to the contents of a `G_LOG_DOMAIN` constant (and fails
/// to build if not defined).
///
/// In order to use this macro, `glib` must be built with the `log_macros`
/// feature enabled and the [`GlibLogger`](struct.GlibLogger.html) must have been
/// initialized using [`GlibLoggerDomain::CrateTarget`](enum.GlibLoggerDomain.html).
///
/// ```no_run
/// static G_LOG_DOMAIN: &str = "my-domain";
///
/// glib::warn!("This will be logged under 'my-domain'");
/// ```
#[macro_export]
#[cfg(any(docsrs, feature = "log_macros"))]
#[cfg_attr(docsrs, doc(cfg(feature = "log_macros")))]
macro_rules! warn {
(target: $target:expr, $($arg:tt)+) => (
$crate::rs_log::log!(target: $target, $crate::rs_log::Level::Warn, $($arg)+);
);
($($arg:tt)+) => (
$crate::rs_log::log!(target: G_LOG_DOMAIN, $crate::rs_log::Level::Warn, $($arg)+);
)
}
// rustdoc-stripper-ignore-next
/// A macro which behaves exactly as `log::info!` except that it sets the
/// current log target to the contents of a `G_LOG_DOMAIN` constant (and fails
/// to build if not defined).
///
/// In order to use this macro, `glib` must be built with the `log_macros`
/// feature enabled and the [`GlibLogger`](struct.GlibLogger.html) must have been
/// initialized using [`GlibLoggerDomain::CrateTarget`](enum.GlibLoggerDomain.html).
///
/// ```no_run
/// static G_LOG_DOMAIN: &str = "my-domain";
///
/// glib::info!("This will be logged under 'my-domain'");
/// ```
#[macro_export]
#[cfg(any(docsrs, feature = "log_macros"))]
#[cfg_attr(docsrs, doc(cfg(feature = "log_macros")))]
macro_rules! info {
(target: $target:expr, $($arg:tt)+) => (
$crate::rs_log::log!(target: $target, $crate::rs_log::Level::Info, $($arg)+);
);
($($arg:tt)+) => (
$crate::rs_log::log!(target: G_LOG_DOMAIN, $crate::rs_log::Level::Info, $($arg)+);
)
}
// rustdoc-stripper-ignore-next
/// A macro which behaves exactly as `log::debug!` except that it sets the
/// current log target to the contents of a `G_LOG_DOMAIN` constant (and fails
/// to build if not defined).
///
/// In order to use this macro, `glib` must be built with the `log_macros`
/// feature enabled and the [`GlibLogger`](struct.GlibLogger.html) must have been
/// initialized using [`GlibLoggerDomain::CrateTarget`](enum.GlibLoggerDomain.html).
///
/// ```no_run
/// static G_LOG_DOMAIN: &str = "my-domain";
///
/// glib::debug!("This will be logged under 'my-domain'");
/// ```
#[macro_export]
#[cfg(any(docsrs, feature = "log_macros"))]
#[cfg_attr(docsrs, doc(cfg(feature = "log_macros")))]
macro_rules! debug {
(target: $target:expr, $($arg:tt)+) => (
$crate::rs_log::log!(target: $target, $crate::rs_log::Level::Debug, $($arg)+);
);
($($arg:tt)+) => (
$crate::rs_log::log!(target: G_LOG_DOMAIN, $crate::rs_log::Level::Debug, $($arg)+);
)
}
// rustdoc-stripper-ignore-next
/// A macro which behaves exactly as `log::trace!` except that it sets the
/// current log target to the contents of a `G_LOG_DOMAIN` constant (and fails
/// to build if not defined).
///
/// In order to use this macro, `glib` must be built with the `log_macros`
/// feature enabled and the [`GlibLogger`](struct.GlibLogger.html) must have been
/// initialized using [`GlibLoggerDomain::CrateTarget`](enum.GlibLoggerDomain.html).
///
/// ```no_run
/// static G_LOG_DOMAIN: &str = "my-domain";
///
/// glib::trace!("This will be logged under 'my-domain'");
/// ```
#[macro_export]
#[cfg(any(docsrs, feature = "log_macros"))]
#[cfg_attr(docsrs, doc(cfg(feature = "log_macros")))]
macro_rules! trace {
(target: $target:expr, $($arg:tt)+) => (
$crate::rs_log::log!(target: $target, $crate::rs_log::Level::Trace, $($arg)+);
);
($($arg:tt)+) => (
$crate::rs_log::log!(target: G_LOG_DOMAIN, $crate::rs_log::Level::Trace, $($arg)+);
)
}
+160
View File
@@ -0,0 +1,160 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! # Examples
//!
//! ```
//! use glib::prelude::*; // or `use gtk::prelude::*;`
//! use glib::ByteArray;
//!
//! let ba = ByteArray::from(b"abc");
//! assert_eq!(ba, "abc".as_bytes());
//! ```
use std::{
borrow::Borrow,
cmp::Ordering,
fmt,
hash::{Hash, Hasher},
ops::Deref,
slice,
};
use crate::translate::*;
wrapper! {
#[doc(alias = "GByteArray")]
pub struct ByteArray(Shared<ffi::GByteArray>);
match fn {
ref => |ptr| ffi::g_byte_array_ref(ptr),
unref => |ptr| ffi::g_byte_array_unref(ptr),
type_ => || ffi::g_byte_array_get_type(),
}
}
impl Deref for ByteArray {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
unsafe {
let self_ptr: *const ffi::GByteArray = self.to_glib_none().0;
let ptr = (*self_ptr).data;
let len = (*self_ptr).len as usize;
debug_assert!(!ptr.is_null() || len == 0);
if ptr.is_null() {
&[]
} else {
slice::from_raw_parts(ptr as *const u8, len)
}
}
}
}
impl AsRef<[u8]> for ByteArray {
#[inline]
fn as_ref(&self) -> &[u8] {
self
}
}
impl<'a, T: ?Sized + Borrow<[u8]> + 'a> From<&'a T> for ByteArray {
fn from(value: &'a T) -> ByteArray {
let value = value.borrow();
unsafe {
let ba = ffi::g_byte_array_new();
ffi::g_byte_array_append(ba, value.as_ptr(), value.len() as u32);
from_glib_full(ba)
}
}
}
impl fmt::Debug for ByteArray {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list().entries(self.as_ref()).finish()
}
}
macro_rules! impl_cmp {
($lhs:ty, $rhs: ty) => {
#[allow(clippy::redundant_slicing)]
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialEq<$rhs> for $lhs {
#[inline]
fn eq(&self, other: &$rhs) -> bool {
self[..].eq(&other[..])
}
}
#[allow(clippy::redundant_slicing)]
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialEq<$lhs> for $rhs {
#[inline]
fn eq(&self, other: &$lhs) -> bool {
self[..].eq(&other[..])
}
}
#[allow(clippy::redundant_slicing)]
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialOrd<$rhs> for $lhs {
#[inline]
fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
self[..].partial_cmp(&other[..])
}
}
#[allow(clippy::redundant_slicing)]
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialOrd<$lhs> for $rhs {
#[inline]
fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
self[..].partial_cmp(&other[..])
}
}
};
}
impl_cmp!(ByteArray, [u8]);
impl_cmp!(ByteArray, &'a [u8]);
impl_cmp!(&'a ByteArray, [u8]);
impl_cmp!(ByteArray, Vec<u8>);
impl_cmp!(&'a ByteArray, Vec<u8>);
impl PartialEq for ByteArray {
fn eq(&self, other: &Self) -> bool {
self[..] == other[..]
}
}
impl Eq for ByteArray {}
impl Hash for ByteArray {
fn hash<H: Hasher>(&self, state: &mut H) {
Hash::hash_slice(&self[..], state)
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use super::*;
#[test]
fn various() {
let ba = ByteArray::from(b"foobar");
assert_eq!(ba, b"foobar" as &[u8]);
}
#[test]
fn hash() {
let b1 = ByteArray::from(b"this is a test");
let b2 = ByteArray::from(b"this is a test");
let b3 = ByteArray::from(b"test");
let mut set = HashSet::new();
set.insert(b1);
assert!(set.contains(&b2));
assert!(!set.contains(&b3));
}
}
+277
View File
@@ -0,0 +1,277 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{
borrow::Borrow,
cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd},
fmt,
hash::{Hash, Hasher},
ops::Deref,
slice,
};
use crate::translate::*;
wrapper! {
// rustdoc-stripper-ignore-next
/// A shared immutable byte slice (the equivalent of `Rc<[u8]>`).
///
/// `From` implementations that take references (e.g. `&[u8]`) copy the
/// data. The `from_static` constructor avoids copying static data.
///
/// ```
/// use glib::Bytes;
///
/// let v = vec![1, 2, 3];
/// let b = Bytes::from(&v);
/// assert_eq!(v, b);
///
/// let s = b"xyz";
/// let b = Bytes::from_static(s);
/// assert_eq!(&s[..], b);
/// ```
#[doc(alias = "GBytes")]
pub struct Bytes(Shared<ffi::GBytes>);
match fn {
ref => |ptr| ffi::g_bytes_ref(ptr),
unref => |ptr| ffi::g_bytes_unref(ptr),
type_ => || ffi::g_bytes_get_type(),
}
}
impl Bytes {
// rustdoc-stripper-ignore-next
/// Copies `data` into a new shared slice.
#[doc(alias = "g_bytes_new")]
#[inline]
fn new<T: AsRef<[u8]>>(data: T) -> Bytes {
let data = data.as_ref();
unsafe { from_glib_full(ffi::g_bytes_new(data.as_ptr() as *const _, data.len())) }
}
// rustdoc-stripper-ignore-next
/// Creates a view into static `data` without copying.
#[doc(alias = "g_bytes_new_static")]
#[inline]
pub fn from_static(data: &'static [u8]) -> Bytes {
unsafe {
from_glib_full(ffi::g_bytes_new_static(
data.as_ptr() as *const _,
data.len(),
))
}
}
// rustdoc-stripper-ignore-next
/// Takes ownership of `data` and creates a new `Bytes` without copying.
pub fn from_owned<T: AsRef<[u8]> + Send + 'static>(data: T) -> Bytes {
let data: Box<T> = Box::new(data);
let (size, data_ptr) = {
let data = (*data).as_ref();
(data.len(), data.as_ptr())
};
unsafe extern "C" fn drop_box<T: AsRef<[u8]> + Send + 'static>(b: ffi::gpointer) {
let _: Box<T> = Box::from_raw(b as *mut _);
}
unsafe {
from_glib_full(ffi::g_bytes_new_with_free_func(
data_ptr as *const _,
size,
Some(drop_box::<T>),
Box::into_raw(data) as *mut _,
))
}
}
}
unsafe impl Send for Bytes {}
unsafe impl Sync for Bytes {}
impl<'a, T: ?Sized + Borrow<[u8]> + 'a> From<&'a T> for Bytes {
#[inline]
fn from(value: &'a T) -> Bytes {
Bytes::new(value.borrow())
}
}
impl fmt::Debug for Bytes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Bytes")
.field("ptr", &ToGlibPtr::<*const _>::to_glib_none(self).0)
.field("data", &&self[..])
.finish()
}
}
impl AsRef<[u8]> for Bytes {
#[inline]
fn as_ref(&self) -> &[u8] {
self
}
}
impl Deref for Bytes {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
unsafe {
let mut len = 0;
let ptr = ffi::g_bytes_get_data(self.to_glib_none().0, &mut len);
if ptr.is_null() || len == 0 {
&[]
} else {
slice::from_raw_parts(ptr as *const u8, len)
}
}
}
}
impl PartialEq for Bytes {
#[doc(alias = "g_bytes_equal")]
#[inline]
fn eq(&self, other: &Self) -> bool {
unsafe {
from_glib(ffi::g_bytes_equal(
ToGlibPtr::<*const _>::to_glib_none(self).0 as *const _,
ToGlibPtr::<*const _>::to_glib_none(other).0 as *const _,
))
}
}
}
impl Eq for Bytes {}
impl PartialOrd for Bytes {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Bytes {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
unsafe {
let ret = ffi::g_bytes_compare(
ToGlibPtr::<*const _>::to_glib_none(self).0 as *const _,
ToGlibPtr::<*const _>::to_glib_none(other).0 as *const _,
);
ret.cmp(&0)
}
}
}
macro_rules! impl_cmp {
($lhs:ty, $rhs: ty) => {
#[allow(clippy::redundant_slicing)]
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialEq<$rhs> for $lhs {
#[inline]
fn eq(&self, other: &$rhs) -> bool {
self[..].eq(&other[..])
}
}
#[allow(clippy::redundant_slicing)]
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialEq<$lhs> for $rhs {
#[inline]
fn eq(&self, other: &$lhs) -> bool {
self[..].eq(&other[..])
}
}
#[allow(clippy::redundant_slicing)]
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialOrd<$rhs> for $lhs {
#[inline]
fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
self[..].partial_cmp(&other[..])
}
}
#[allow(clippy::redundant_slicing)]
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialOrd<$lhs> for $rhs {
#[inline]
fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
self[..].partial_cmp(&other[..])
}
}
};
}
impl_cmp!(Bytes, [u8]);
impl_cmp!(Bytes, &'a [u8]);
impl_cmp!(&'a Bytes, [u8]);
impl_cmp!(Bytes, Vec<u8>);
impl_cmp!(&'a Bytes, Vec<u8>);
impl Hash for Bytes {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.len().hash(state);
Hash::hash_slice(self, state)
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use super::*;
#[test]
fn eq() {
let abc: &[u8] = b"abc";
let def: &[u8] = b"def";
let a1 = Bytes::from(abc);
let a2 = Bytes::from(abc);
let d = Bytes::from(def);
assert_eq!(a1, a2);
assert_eq!(def, d);
assert_ne!(a1, d);
assert_ne!(a1, def);
}
#[test]
fn ord() {
let abc: &[u8] = b"abc";
let def: &[u8] = b"def";
let a = Bytes::from(abc);
let d = Bytes::from(def);
assert!(a < d);
assert!(a < def);
assert!(abc < d);
assert!(d > a);
assert!(d > abc);
assert!(def > a);
}
#[test]
fn hash() {
let b1 = Bytes::from(b"this is a test");
let b2 = Bytes::from(b"this is a test");
let b3 = Bytes::from(b"test");
let mut set = HashSet::new();
set.insert(b1);
assert!(set.contains(&b2));
assert!(!set.contains(&b3));
}
#[test]
fn from_static() {
let b1 = Bytes::from_static(b"this is a test");
let b2 = Bytes::from(b"this is a test");
assert_eq!(b1, b2);
}
#[test]
fn from_owned() {
let b = Bytes::from_owned(vec![1, 2, 3]);
assert_eq!(b, [1u8, 2u8, 3u8].as_ref());
}
}
+220
View File
@@ -0,0 +1,220 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{convert::TryFrom, num::TryFromIntError};
use libc::{c_char, c_uchar};
use crate::translate::*;
// rustdoc-stripper-ignore-next
/// Wrapper for values where C functions expect a plain C `char`
///
/// Consider the following C function prototype from glib:
///
/// ```C
/// void g_key_file_set_list_separator (GKeyFile *key_file, gchar separator);
/// ```
///
/// This function plainly expects a byte as the `separator` argument. However,
/// having this function exposed to Rust as the following would be inconvenient:
///
/// ```ignore
/// impl KeyFile {
/// pub fn set_list_separator(&self, separator: libc:c_char) { }
/// }
/// ```
///
/// This would be inconvenient because users would have to do the conversion from a Rust `char` to an `libc::c_char` by hand, which is just a type alias
/// for `i8` on most system.
///
/// This `Char` type is a wrapper over an `libc::c_char`, so that we can pass it to Glib or C functions.
/// The check for whether a Rust `char` (a Unicode scalar value) actually fits in a `libc::c_char` is
/// done in the `new` function; see its documentation for details.
///
/// The inner `libc::c_char` (which is equivalent to `i8`) can be extracted with `.0`, or
/// by calling `my_char.into_glib()`.
///
/// # Examples
/// ```
/// use glib::Char;
/// use std::convert::TryFrom;
///
/// Char::from(b'a');
/// Char::try_from('a').unwrap();
/// assert!(Char::try_from('☔').is_err());
/// ```
///
/// ```ignore
/// extern "C" fn have_a_byte(b: libc::c_char);
///
/// have_a_byte(Char::from(b'a').into_glib());
/// ```
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Char(pub c_char);
impl TryFrom<char> for Char {
type Error = TryFromIntError;
#[allow(clippy::unnecessary_cast)]
fn try_from(c: char) -> Result<Char, Self::Error> {
Ok(Self(u8::try_from(u32::from(c))? as c_char))
}
}
impl From<Char> for char {
fn from(c: Char) -> char {
c.0 as u8 as char
}
}
impl From<u8> for Char {
#[allow(clippy::unnecessary_cast)]
fn from(c: u8) -> Char {
Char(c as c_char)
}
}
impl From<Char> for u8 {
#[allow(clippy::unnecessary_cast)]
fn from(c: Char) -> u8 {
c.0 as u8
}
}
#[doc(hidden)]
impl FromGlib<c_char> for Char {
#[inline]
unsafe fn from_glib(value: c_char) -> Self {
Self(value)
}
}
#[doc(hidden)]
impl IntoGlib for Char {
type GlibType = c_char;
#[inline]
fn into_glib(self) -> c_char {
self.0
}
}
// rustdoc-stripper-ignore-next
/// Wrapper for values where C functions expect a plain C `unsigned char`
///
/// This `UChar` type is a wrapper over an `libc::c_uchar`, so that we can pass it to Glib or C functions.
/// The check for whether a Rust `char` (a Unicode scalar value) actually fits in a `libc::c_uchar` is
/// done in the `new` function; see its documentation for details.
///
/// The inner `libc::c_uchar` (which is equivalent to `u8`) can be extracted with `.0`, or
/// by calling `my_char.into_glib()`.
///
/// # Examples
/// ```
/// use glib::UChar;
/// use std::convert::TryFrom;
///
/// UChar::from(b'a');
/// UChar::try_from('a').unwrap();
/// assert!(UChar::try_from('☔').is_err());
/// ```
///
/// ```ignore
/// extern "C" fn have_a_byte(b: libc::c_uchar);
///
/// have_a_byte(UChar::from(b'a').into_glib());
/// ```
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct UChar(pub c_uchar);
impl TryFrom<char> for UChar {
type Error = TryFromIntError;
#[allow(clippy::unnecessary_cast)]
fn try_from(c: char) -> Result<UChar, Self::Error> {
Ok(Self(u8::try_from(u32::from(c))? as c_uchar))
}
}
impl From<UChar> for char {
fn from(c: UChar) -> char {
c.0 as _
}
}
impl From<u8> for UChar {
#[allow(clippy::unnecessary_cast)]
fn from(c: u8) -> UChar {
UChar(c as _)
}
}
impl From<UChar> for u8 {
fn from(c: UChar) -> u8 {
c.0 as _
}
}
#[doc(hidden)]
impl FromGlib<c_uchar> for UChar {
#[inline]
unsafe fn from_glib(value: c_uchar) -> Self {
Self(value)
}
}
#[doc(hidden)]
impl IntoGlib for UChar {
type GlibType = c_uchar;
#[inline]
fn into_glib(self) -> c_uchar {
self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(clippy::unnecessary_cast)]
fn converts_single_byte_chars() {
assert_eq!(Char::try_from(0 as char), Ok(Char(0 as c_char)));
assert_eq!(UChar::try_from(0 as char), Ok(UChar(0 as c_uchar)));
assert_eq!(UChar::try_from(255 as char), Ok(UChar(255 as c_uchar)));
assert_eq!(UChar::try_from('ñ'), Ok(UChar(241 as c_uchar)));
}
#[test]
fn refuses_multibyte_chars() {
assert!(Char::try_from('☔').is_err()); // no umbrella for you
assert!(UChar::try_from('☔').is_err());
}
#[test]
#[allow(clippy::unnecessary_cast)]
fn into_i8() {
assert_eq!(Char::from(b'A').into_glib(), 65 as c_char);
}
#[test]
#[allow(clippy::unnecessary_cast)]
fn into_u8() {
assert_eq!(UChar::from(b'A').into_glib(), 65 as c_uchar);
}
#[test]
#[allow(clippy::unnecessary_cast)]
fn into_char() {
assert_eq!(char::from(Char(65 as c_char)), 'A');
assert_eq!('ñ', UChar(241 as c_uchar).into());
}
#[test]
#[allow(clippy::unnecessary_cast)]
fn convert_from_glib() {
assert_eq!(Char(65 as c_char), unsafe { from_glib(65 as c_char) });
assert_eq!(UChar(241 as c_uchar), unsafe { from_glib(241 as c_uchar) });
}
}
+74
View File
@@ -0,0 +1,74 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::vec::Vec;
use libc::size_t;
use crate::{translate::*, Checksum};
impl Checksum {
#[doc(alias = "g_checksum_get_digest")]
#[doc(alias = "get_digest")]
pub fn digest(self) -> Vec<u8> {
unsafe {
//Don't forget update when `ChecksumType` contains type bigger that Sha512.
let mut digest_len: size_t = 512 / 8;
let mut vec = Vec::with_capacity(digest_len as _);
ffi::g_checksum_get_digest(
mut_override(self.to_glib_none().0),
vec.as_mut_ptr(),
&mut digest_len,
);
vec.set_len(digest_len);
vec
}
}
#[doc(alias = "g_checksum_get_string")]
#[doc(alias = "get_string")]
pub fn string(self) -> Option<String> {
unsafe {
from_glib_none(ffi::g_checksum_get_string(mut_override(
self.to_glib_none().0,
)))
}
}
}
#[cfg(test)]
mod tests {
use crate::{Checksum, ChecksumType};
const CS_TYPE: ChecksumType = ChecksumType::Md5;
const CS_VALUE: &str = "fc3ff98e8c6a0d3087d515c0473f8677";
const CS_SLICE: &[u8] = &[
0xfc, 0x3f, 0xf9, 0x8e, 0x8c, 0x6a, 0x0d, 0x30, 0x87, 0xd5, 0x15, 0xc0, 0x47, 0x3f, 0x86,
0x77,
];
#[test]
fn update() {
let mut cs = Checksum::new(CS_TYPE).unwrap();
cs.update(b"hello world!");
assert_eq!(cs.string().unwrap(), CS_VALUE);
}
#[test]
fn update_multi_call() {
let mut cs = Checksum::new(CS_TYPE).unwrap();
cs.update(b"hello ");
cs.update(b"world!");
assert_eq!(cs.string().unwrap(), CS_VALUE);
}
#[test]
#[doc(alias = "get_digest")]
fn digest() {
let mut cs = Checksum::new(CS_TYPE).unwrap();
cs.update(b"hello world!");
let vec = cs.digest();
assert_eq!(vec, CS_SLICE);
}
}
+101
View File
@@ -0,0 +1,101 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{
marker::PhantomData,
rc::{self, Rc},
sync::{self, Arc},
};
// rustdoc-stripper-ignore-next
/// Trait for generalizing downgrading a strong reference to a weak reference.
pub trait Downgrade
where
Self: Sized,
{
// rustdoc-stripper-ignore-next
/// Weak reference type.
type Weak;
// rustdoc-stripper-ignore-next
/// Downgrade to a weak reference.
fn downgrade(&self) -> Self::Weak;
}
// rustdoc-stripper-ignore-next
/// Trait for generalizing upgrading a weak reference to a strong reference.
pub trait Upgrade
where
Self: Sized,
{
// rustdoc-stripper-ignore-next
/// Strong reference type.
type Strong;
// rustdoc-stripper-ignore-next
/// Try upgrading a weak reference to a strong reference.
fn upgrade(&self) -> Option<Self::Strong>;
}
impl<T: Downgrade + crate::ObjectType> Upgrade for crate::WeakRef<T> {
type Strong = T;
fn upgrade(&self) -> Option<Self::Strong> {
self.upgrade()
}
}
impl<T> Downgrade for PhantomData<T> {
type Weak = PhantomData<T>;
fn downgrade(&self) -> Self::Weak {
PhantomData
}
}
impl<T: Downgrade> Downgrade for &T {
type Weak = T::Weak;
fn downgrade(&self) -> Self::Weak {
T::downgrade(*self)
}
}
impl<T> Downgrade for Arc<T> {
type Weak = sync::Weak<T>;
fn downgrade(&self) -> Self::Weak {
Arc::downgrade(self)
}
}
impl<T> Upgrade for PhantomData<T> {
type Strong = PhantomData<T>;
fn upgrade(&self) -> Option<Self::Strong> {
Some(PhantomData)
}
}
impl<T> Upgrade for sync::Weak<T> {
type Strong = Arc<T>;
fn upgrade(&self) -> Option<Self::Strong> {
self.upgrade()
}
}
impl<T> Downgrade for Rc<T> {
type Weak = rc::Weak<T>;
fn downgrade(&self) -> Self::Weak {
Rc::downgrade(self)
}
}
impl<T> Upgrade for rc::Weak<T> {
type Strong = Rc<T>;
fn upgrade(&self) -> Option<Self::Strong> {
self.upgrade()
}
}
+447
View File
@@ -0,0 +1,447 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// TODO: support marshaller.
use std::{mem, ptr, slice};
use libc::{c_uint, c_void};
use crate::{prelude::*, translate::*, value::FromValue, Type, Value};
wrapper! {
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
#[doc(alias = "GClosure")]
pub struct Closure(Shared<gobject_ffi::GClosure>);
match fn {
ref => |ptr| {
gobject_ffi::g_closure_ref(ptr);
gobject_ffi::g_closure_sink(ptr);
},
unref => |ptr| gobject_ffi::g_closure_unref(ptr),
type_ => || gobject_ffi::g_closure_get_type(),
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct RustClosure(Closure);
impl RustClosure {
// rustdoc-stripper-ignore-next
/// Creates a new closure around a Rust closure.
///
/// See [`glib::closure!`](macro@crate::closure) for a way to create a closure with concrete
/// types.
///
/// # Panics
///
/// Invoking the closure with wrong argument types or returning the wrong return value type
/// will panic.
///
/// # Example
///
/// ```
/// use glib::prelude::*;
///
/// let closure = glib::RustClosure::new(|values| {
/// let x = values[0].get::<i32>().unwrap();
/// Some((x + 1).to_value())
/// });
///
/// assert_eq!(
/// closure.invoke::<i32>(&[&1i32]),
/// 2,
/// );
/// ```
#[doc(alias = "g_closure_new")]
pub fn new<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(callback: F) -> Self {
Self(Closure::new(callback))
}
// rustdoc-stripper-ignore-next
/// Creates a new closure around a Rust closure.
///
/// See [`glib::closure_local!`](crate::closure_local) for a way to create a closure with
/// concrete types.
///
/// # Panics
///
/// Invoking the closure with wrong argument types or returning the wrong return value type
/// will panic.
///
/// Invoking the closure from a different thread than this one will panic.
#[doc(alias = "g_closure_new")]
pub fn new_local<F: Fn(&[Value]) -> Option<Value> + 'static>(callback: F) -> Self {
Self(Closure::new_local(callback))
}
// rustdoc-stripper-ignore-next
/// Invokes the closure with the given arguments.
///
/// For invalidated closures this returns the "default" value of the return type. For nullable
/// types this is `None`, which means that e.g. requesting `R = String` will panic will `R =
/// Option<String>` will return `None`.
///
/// # Panics
///
/// The argument types and return value type must match the ones expected by the closure or
/// otherwise this function panics.
#[doc(alias = "g_closure_invoke")]
pub fn invoke<R: TryFromClosureReturnValue>(&self, values: &[&dyn ToValue]) -> R {
let values = values
.iter()
.copied()
.map(ToValue::to_value)
.collect::<smallvec::SmallVec<[_; 10]>>();
R::try_from_closure_return_value(self.invoke_with_values(R::static_type(), &values))
.expect("Invalid return value")
}
// rustdoc-stripper-ignore-next
/// Invokes the closure with the given arguments.
///
/// For invalidated closures this returns the "default" value of the return type.
///
/// # Panics
///
/// The argument types and return value type must match the ones expected by the closure or
/// otherwise this function panics.
#[doc(alias = "g_closure_invoke")]
pub fn invoke_with_values(&self, return_type: Type, values: &[Value]) -> Option<Value> {
unsafe { self.0.invoke_with_values(return_type, values) }
}
// rustdoc-stripper-ignore-next
/// Invalidates the closure.
///
/// Invoking an invalidated closure has no effect.
#[doc(alias = "g_closure_invalidate")]
pub fn invalidate(&self) {
self.0.invalidate();
}
}
impl From<RustClosure> for Closure {
#[inline]
fn from(c: RustClosure) -> Self {
c.0
}
}
impl AsRef<Closure> for RustClosure {
#[inline]
fn as_ref(&self) -> &Closure {
&self.0
}
}
impl AsRef<Closure> for Closure {
#[inline]
fn as_ref(&self) -> &Closure {
self
}
}
impl Closure {
// rustdoc-stripper-ignore-next
/// Creates a new closure around a Rust closure.
///
/// Note that [`RustClosure`] provides more convenient and non-unsafe API for invoking
/// closures. This type mostly exists for FFI interop.
///
/// # Panics
///
/// Invoking the closure with wrong argument types or returning the wrong return value type
/// will panic.
///
///
/// # Example
///
/// ```
/// use glib::prelude::*;
///
/// let closure = glib::Closure::new(|values| {
/// let x = values[0].get::<i32>().unwrap();
/// Some((x + 1).to_value())
/// });
///
/// // Invoking non-Rust closures is unsafe because of possibly missing
/// // argument and return value type checks.
/// let res = unsafe {
/// closure
/// .invoke_with_values(glib::Type::I32, &[1i32.to_value()])
/// .and_then(|v| v.get::<i32>().ok())
/// .expect("Invalid return value")
/// };
///
/// assert_eq!(res, 2);
/// ```
#[doc(alias = "g_closure_new")]
pub fn new<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(callback: F) -> Self {
unsafe { Self::new_unsafe(callback) }
}
// rustdoc-stripper-ignore-next
/// Creates a new closure around a Rust closure.
///
/// Note that [`RustClosure`] provides more convenient and non-unsafe API for invoking
/// closures. This type mostly exists for FFI interop.
///
/// # Panics
///
/// Invoking the closure with wrong argument types or returning the wrong return value type
/// will panic.
///
/// Invoking the closure from a different thread than this one will panic.
#[doc(alias = "g_closure_new")]
pub fn new_local<F: Fn(&[Value]) -> Option<Value> + 'static>(callback: F) -> Self {
let callback = crate::thread_guard::ThreadGuard::new(callback);
unsafe { Self::new_unsafe(move |values| (callback.get_ref())(values)) }
}
// rustdoc-stripper-ignore-next
/// Creates a new closure around a Rust closure.
///
/// # Safety
///
/// The captured variables of the closure must stay valid as long as the return value of this
/// constructor does, and it must be valid to call the closure from any thread that is used by
/// callers.
#[doc(alias = "g_closure_new")]
pub unsafe fn new_unsafe<F: Fn(&[Value]) -> Option<Value>>(callback: F) -> Self {
unsafe extern "C" fn marshal<F>(
_closure: *mut gobject_ffi::GClosure,
return_value: *mut gobject_ffi::GValue,
n_param_values: c_uint,
param_values: *const gobject_ffi::GValue,
_invocation_hint: *mut c_void,
marshal_data: *mut c_void,
) where
F: Fn(&[Value]) -> Option<Value>,
{
let values = if n_param_values == 0 {
&[]
} else {
slice::from_raw_parts(param_values as *const _, n_param_values as usize)
};
let callback: &F = &*(marshal_data as *mut _);
let result = callback(values);
if return_value.is_null() {
assert!(
result.is_none(),
"Closure returned a return value but the caller did not expect one"
);
} else {
let return_value = &mut *(return_value as *mut Value);
match result {
Some(result) => {
assert!(
result.type_().is_a(return_value.type_()),
"Closure returned a value of type {} but caller expected {}",
result.type_(),
return_value.type_()
);
*return_value = result;
}
None if return_value.type_() == Type::INVALID => (),
None => {
panic!(
"Closure returned no value but the caller expected a value of type {}",
return_value.type_()
);
}
}
}
}
unsafe extern "C" fn finalize<F>(
notify_data: *mut c_void,
_closure: *mut gobject_ffi::GClosure,
) where
F: Fn(&[Value]) -> Option<Value>,
{
let _callback: Box<F> = Box::from_raw(notify_data as *mut _);
// callback is dropped here.
}
// Due to bitfields we have to do our own calculations here for the size of the GClosure:
// - 4: 32 bits in guint bitfields at the beginning
// - padding due to alignment needed for the following pointer
// - 3 * size_of<*mut c_void>: 3 pointers
// We don't store any custom data ourselves in the GClosure
let size = u32::max(4, mem::align_of::<*mut c_void>() as u32)
+ 3 * mem::size_of::<*mut c_void>() as u32;
let closure = gobject_ffi::g_closure_new_simple(size, ptr::null_mut());
let callback = Box::new(callback);
let ptr: *mut F = Box::into_raw(callback);
let ptr: *mut c_void = ptr as *mut _;
gobject_ffi::g_closure_set_meta_marshal(closure, ptr, Some(marshal::<F>));
gobject_ffi::g_closure_add_finalize_notifier(closure, ptr, Some(finalize::<F>));
from_glib_none(closure)
}
// rustdoc-stripper-ignore-next
/// Invokes the closure with the given arguments.
///
/// For invalidated closures this returns the "default" value of the return type.
///
/// # Safety
///
/// The argument types and return value type must match the ones expected by the closure or
/// otherwise the behaviour is undefined.
///
/// Closures created from Rust via e.g. [`Closure::new`] will panic on type mismatches but
/// this is not guaranteed for closures created from other languages.
#[doc(alias = "g_closure_invoke")]
pub unsafe fn invoke_with_values(&self, return_type: Type, values: &[Value]) -> Option<Value> {
let mut result = if return_type == Type::UNIT {
Value::uninitialized()
} else {
Value::from_type(return_type)
};
let result_ptr = if return_type == Type::UNIT {
ptr::null_mut()
} else {
result.to_glib_none_mut().0
};
gobject_ffi::g_closure_invoke(
self.to_glib_none().0,
result_ptr,
values.len() as u32,
mut_override(values.as_ptr()) as *mut gobject_ffi::GValue,
ptr::null_mut(),
);
if return_type == Type::UNIT {
None
} else {
Some(result)
}
}
// rustdoc-stripper-ignore-next
/// Invalidates the closure.
///
/// Invoking an invalidated closure has no effect.
#[doc(alias = "g_closure_invalidate")]
pub fn invalidate(&self) {
unsafe {
gobject_ffi::g_closure_invalidate(self.to_glib_none().0);
}
}
}
pub trait IntoClosureReturnValue {
fn into_closure_return_value(self) -> Option<Value>;
}
impl IntoClosureReturnValue for () {
#[inline]
fn into_closure_return_value(self) -> Option<Value> {
None
}
}
impl<T: Into<Value>> IntoClosureReturnValue for T {
#[inline]
fn into_closure_return_value(self) -> Option<Value> {
Some(self.into())
}
}
pub trait TryFromClosureReturnValue: StaticType + Sized + 'static {
fn try_from_closure_return_value(v: Option<Value>) -> Result<Self, crate::BoolError>;
}
impl TryFromClosureReturnValue for () {
#[inline]
fn try_from_closure_return_value(v: Option<Value>) -> Result<Self, crate::BoolError> {
match v {
None => Ok(()),
Some(v) => Err(bool_error!(
"Invalid return value: expected (), got {}",
v.type_()
)),
}
}
}
impl<T: for<'a> FromValue<'a> + StaticType + 'static> TryFromClosureReturnValue for T {
#[inline]
fn try_from_closure_return_value(v: Option<Value>) -> Result<Self, crate::BoolError> {
v.ok_or_else(|| {
bool_error!(
"Invalid return value: expected {}, got ()",
T::static_type()
)
})
.and_then(|v| {
v.get_owned::<T>().map_err(|_| {
bool_error!(
"Invalid return value: expected {}, got {}",
T::static_type(),
v.type_()
)
})
})
}
}
unsafe impl Send for Closure {}
unsafe impl Sync for Closure {}
#[cfg(test)]
mod tests {
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc,
};
use super::*;
#[allow(clippy::unnecessary_wraps)]
fn closure_fn(values: &[Value]) -> Option<Value> {
assert_eq!(values.len(), 2);
let string_arg = values[0].get::<&str>();
assert_eq!(string_arg, Ok("test"));
let int_arg = values[1].get::<i32>();
assert_eq!(int_arg, Ok(42));
Some(24.to_value())
}
#[test]
fn test_closure() {
let call_count = Arc::new(AtomicUsize::new(0));
let count = call_count.clone();
let closure = RustClosure::new(move |values| {
count.fetch_add(1, Ordering::Relaxed);
assert_eq!(values.len(), 2);
let string_arg = values[0].get::<&str>();
assert_eq!(string_arg, Ok("test"));
let int_arg = values[1].get::<i32>();
assert_eq!(int_arg, Ok(42));
None
});
closure.invoke::<()>(&[&"test", &42]);
assert_eq!(call_count.load(Ordering::Relaxed), 1);
closure.invoke::<()>(&[&"test", &42]);
assert_eq!(call_count.load(Ordering::Relaxed), 2);
closure.invalidate();
closure.invoke::<()>(&[&"test", &42]);
assert_eq!(call_count.load(Ordering::Relaxed), 2);
let closure = RustClosure::new(closure_fn);
let result = closure.invoke::<i32>(&[&"test", &42]);
assert_eq!(result, 24);
closure.invalidate();
let result = closure.invoke::<i32>(&[&"test", &42]);
assert_eq!(result, 0);
}
}
@@ -0,0 +1,894 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{iter::FusedIterator, marker::PhantomData, mem, ptr};
use crate::translate::*;
// rustdoc-stripper-ignore-next
/// A list of items of type `T`.
///
/// Behaves like an `Iterator<Item = T>` but allows modifications.
#[repr(transparent)]
pub struct List<T: TransparentPtrType> {
ptr: Option<ptr::NonNull<ffi::GList>>,
phantom: PhantomData<T>,
}
#[doc(hidden)]
unsafe impl<T: TransparentPtrType> TransparentPtrType for List<T> {}
#[doc(hidden)]
impl<T: TransparentPtrType> GlibPtrDefault for List<T> {
type GlibType = *mut ffi::GList;
}
unsafe impl<T: Send + TransparentPtrType> Send for List<T> {}
unsafe impl<T: Sync + TransparentPtrType> Sync for List<T> {}
impl<T: TransparentPtrType> List<T> {
// rustdoc-stripper-ignore-next
/// Create a new `List` around a list.
#[inline]
pub unsafe fn from_glib_none(list: *const ffi::GList) -> List<T> {
// Need to copy the whole list
let list = if mem::needs_drop::<T>() {
unsafe extern "C" fn copy_item<T: TransparentPtrType>(
ptr: ffi::gconstpointer,
_user_data: ffi::gpointer,
) -> ffi::gpointer {
let mut item = mem::ManuallyDrop::new(
(*(&ptr as *const ffi::gconstpointer as *const T)).clone(),
);
*(&mut *item as *mut T as *mut *mut T::GlibType) as ffi::gpointer
}
ffi::g_list_copy_deep(mut_override(list), Some(copy_item::<T>), ptr::null_mut())
} else {
ffi::g_list_copy(mut_override(list))
};
List {
ptr: ptr::NonNull::new(list),
phantom: PhantomData,
}
}
// rustdoc-stripper-ignore-next
/// Create a new `List` around a list.
#[inline]
pub unsafe fn from_glib_container(list: *mut ffi::GList) -> List<T> {
// Need to copy all items as we only own the container
if mem::needs_drop::<T>() {
unsafe extern "C" fn copy_item<T: TransparentPtrType>(
ptr: ffi::gpointer,
_user_data: ffi::gpointer,
) {
let item = (*(&ptr as *const ffi::gpointer as *const T)).clone();
ptr::write(ptr as *mut T, item);
}
ffi::g_list_foreach(list, Some(copy_item::<T>), ptr::null_mut());
}
List {
ptr: ptr::NonNull::new(list),
phantom: PhantomData,
}
}
// rustdoc-stripper-ignore-next
/// Create a new `List` around a list.
#[inline]
pub unsafe fn from_glib_full(list: *mut ffi::GList) -> List<T> {
List {
ptr: ptr::NonNull::new(list),
phantom: PhantomData,
}
}
// rustdoc-stripper-ignore-next
/// Creates a new empty list.
#[inline]
pub fn new() -> Self {
List {
ptr: None,
phantom: PhantomData,
}
}
// rustdoc-stripper-ignore-next
/// Create a non-destructive iterator over the `List`.
#[inline]
pub fn iter(&self) -> Iter<T> {
Iter::new(self)
}
// rustdoc-stripper-ignore-next
/// Create a non-destructive mutable iterator over the `List`.
#[inline]
pub fn iter_mut(&mut self) -> IterMut<T> {
IterMut::new(self)
}
// rustdoc-stripper-ignore-next
/// Check if the list is empty.
///
/// This operation is `O(1)`.
#[inline]
pub fn is_empty(&self) -> bool {
self.ptr.is_none()
}
// rustdoc-stripper-ignore-next
/// Returns the length of the list.
///
/// This operation is `O(n)`.
#[inline]
#[doc(alias = "g_list_length")]
pub fn len(&self) -> usize {
self.iter().count()
}
// rustdoc-stripper-ignore-next
/// Returns a reference to the first item of the list, if any.
///
/// This operation is `O(1)`.
#[inline]
#[doc(alias = "g_list_first")]
pub fn front(&self) -> Option<&T> {
match self.ptr {
None => None,
Some(cur) => unsafe {
let item = &*(&cur.as_ref().data as *const ffi::gpointer as *const T);
Some(item)
},
}
}
// rustdoc-stripper-ignore-next
/// Returns a mutable reference to the first item of the list, if any.
///
/// This operation is `O(1)`.
#[inline]
#[doc(alias = "g_list_first")]
pub fn front_mut(&mut self) -> Option<&mut T> {
match self.ptr {
None => None,
Some(mut cur) => unsafe {
let item = &mut *(&mut cur.as_mut().data as *mut ffi::gpointer as *mut T);
Some(item)
},
}
}
// rustdoc-stripper-ignore-next
/// Removes the front item from the list, if any.
///
/// This operation is `O(1)`.
#[inline]
pub fn pop_front(&mut self) -> Option<T> {
match self.ptr {
None => None,
Some(mut cur) => unsafe {
self.ptr = ptr::NonNull::new(cur.as_ref().next);
if let Some(mut next) = self.ptr {
next.as_mut().prev = ptr::null_mut();
}
let item = ptr::read(&mut cur.as_mut().data as *mut ffi::gpointer as *mut T);
ffi::g_list_free_1(cur.as_ptr());
Some(item)
},
}
}
// rustdoc-stripper-ignore-next
/// Prepends the new item to the front of the list.
///
/// This operation is `O(1)`.
#[inline]
#[doc(alias = "g_list_prepend")]
pub fn push_front(&mut self, item: T) {
unsafe {
let ptr = self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut());
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_list_prepend(
ptr,
*(&mut *mem::ManuallyDrop::new(item) as *mut T as *mut *mut T::GlibType)
as ffi::gpointer,
)));
}
}
// rustdoc-stripper-ignore-next
/// Returns a reference to the last item of the list, if any.
///
/// This operation is `O(n)`.
#[inline]
#[doc(alias = "g_list_last")]
pub fn back(&self) -> Option<&T> {
unsafe {
let ptr = match self.ptr {
None => return None,
Some(ptr) => ptr.as_ptr(),
};
let last_ptr = ffi::g_list_last(ptr);
let item = &*(&(*last_ptr).data as *const ffi::gpointer as *const T);
Some(item)
}
}
// rustdoc-stripper-ignore-next
/// Returns a mutable reference to the last item of the list, if any.
///
/// This operation is `O(n)`.
#[inline]
#[doc(alias = "g_list_last")]
pub fn back_mut(&mut self) -> Option<&mut T> {
unsafe {
let ptr = match self.ptr {
None => return None,
Some(ptr) => ptr.as_ptr(),
};
let last_ptr = ffi::g_list_last(ptr);
let item = &mut *(&mut (*last_ptr).data as *mut ffi::gpointer as *mut T);
Some(item)
}
}
// rustdoc-stripper-ignore-next
/// Removes the back item from the list, if any.
///
/// This operation is `O(n)`.
#[inline]
pub fn pop_back(&mut self) -> Option<T> {
unsafe {
let ptr = match self.ptr {
None => return None,
Some(ptr) => ptr.as_ptr(),
};
let last_ptr = ffi::g_list_last(ptr);
let item = ptr::read(&mut (*last_ptr).data as *mut ffi::gpointer as *mut T);
self.ptr = ptr::NonNull::new(ffi::g_list_delete_link(ptr, last_ptr));
Some(item)
}
}
// rustdoc-stripper-ignore-next
/// Appends the new item to the back of the list.
///
/// this operation is `O(n)`.
#[inline]
#[doc(alias = "g_list_append")]
pub fn push_back(&mut self, item: T) {
unsafe {
let ptr = self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut());
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_list_append(
ptr,
*(&mut *mem::ManuallyDrop::new(item) as *mut T as *mut *mut T::GlibType)
as ffi::gpointer,
)));
}
}
// rustdoc-stripper-ignore-next
/// Reverse the list.
///
/// This operation is `O(n)`.
#[inline]
#[doc(alias = "g_list_reverse")]
pub fn reverse(&mut self) {
unsafe {
let ptr = match self.ptr {
None => return,
Some(ptr) => ptr.as_ptr(),
};
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_list_reverse(ptr)));
}
}
// rustdoc-stripper-ignore-next
/// Sorts the list.
///
/// This operation is `O(n * log n)`.
#[inline]
#[doc(alias = "g_list_sort")]
pub fn sort(&mut self)
where
T: Ord,
{
self.sort_by(|a, b| a.cmp(b));
}
// rustdoc-stripper-ignore-next
/// Sorts the list.
///
/// This operation is `O(n * log n)`.
#[inline]
#[doc(alias = "g_list_sort")]
pub fn sort_by<F: FnMut(&T, &T) -> std::cmp::Ordering>(&mut self, mut f: F) {
unsafe {
let ptr = match self.ptr {
None => return,
Some(ptr) => ptr.as_ptr(),
};
unsafe extern "C" fn func<
T: TransparentPtrType,
F: FnMut(&T, &T) -> std::cmp::Ordering,
>(
a: ffi::gconstpointer,
b: ffi::gconstpointer,
user_data: ffi::gpointer,
) -> i32 {
let f = &mut *(user_data as *mut F);
let a = &*(&a as *const ffi::gconstpointer as *const T);
let b = &*(&b as *const ffi::gconstpointer as *const T);
f(a, b).into_glib()
}
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_list_sort_with_data(
ptr,
Some(func::<T, F>),
&mut f as *mut F as ffi::gpointer,
)));
}
}
// rustdoc-stripper-ignore-next
/// Removes all items from the list.
#[inline]
pub fn clear(&mut self) {
*self = Self::new();
}
// rustdoc-stripper-ignore-next
/// Only keeps the item in the list for which `f` returns `true`.
#[inline]
pub fn retain(&mut self, mut f: impl FnMut(&T) -> bool) {
if let Some(head) = self.ptr {
unsafe {
let mut ptr = head.as_ptr();
while !ptr.is_null() {
let item = &*((*ptr).data as *const ffi::gpointer as *const T);
let next = (*ptr).next;
if !f(item) {
ptr::drop_in_place(&mut (*ptr).data as *mut ffi::gpointer as *mut T);
self.ptr = ptr::NonNull::new(ffi::g_list_delete_link(head.as_ptr(), ptr));
}
ptr = next;
}
}
}
}
// rustdoc-stripper-ignore-next
/// Returns the underlying pointer.
#[inline]
pub fn as_ptr(&self) -> *const ffi::GList {
self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut())
}
// rustdoc-stripper-ignore-next
/// Returns the underlying pointer.
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut ffi::GList {
self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut())
}
// rustdoc-stripper-ignore-next
/// Consumes the list and returns the underlying pointer.
#[inline]
pub fn into_raw(mut self) -> *mut ffi::GList {
self.ptr
.take()
.map(|p| p.as_ptr())
.unwrap_or(ptr::null_mut())
}
}
impl<T: TransparentPtrType> Default for List<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: TransparentPtrType> Clone for List<T> {
fn clone(&self) -> Self {
unsafe { Self::from_glib_none(self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut())) }
}
}
impl<T: TransparentPtrType> Drop for List<T> {
#[inline]
fn drop(&mut self) {
if let Some(ptr) = self.ptr {
unsafe {
if mem::needs_drop::<T>() {
unsafe extern "C" fn drop_item<T: TransparentPtrType>(mut ptr: ffi::gpointer) {
ptr::drop_in_place(&mut ptr as *mut ffi::gpointer as *mut T);
}
ffi::g_list_free_full(ptr.as_ptr(), Some(drop_item::<T>));
} else {
ffi::g_list_free(ptr.as_ptr());
}
}
}
}
}
impl<T: TransparentPtrType> std::iter::FromIterator<T> for List<T> {
#[inline]
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
unsafe {
let mut iter = iter.into_iter();
let first = match iter.next() {
None => return Self::new(),
Some(first) => first,
};
let list = ffi::g_list_prepend(
ptr::null_mut(),
*(&mut *mem::ManuallyDrop::new(first) as *mut T as *mut *mut T::GlibType)
as ffi::gpointer,
);
let mut tail = list;
for item in iter {
let new_tail = ffi::g_list_alloc();
(*new_tail).data = *(&mut *mem::ManuallyDrop::new(item) as *mut T
as *mut *mut T::GlibType) as ffi::gpointer;
(*new_tail).prev = tail;
(*new_tail).next = ptr::null_mut();
(*tail).next = new_tail;
tail = new_tail;
}
Self::from_glib_full(list)
}
}
}
impl<'a, T: TransparentPtrType> std::iter::IntoIterator for &'a List<T> {
type Item = &'a T;
type IntoIter = Iter<'a, T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, T: TransparentPtrType> std::iter::IntoIterator for &'a mut List<T> {
type Item = &'a mut T;
type IntoIter = IterMut<'a, T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
impl<T: TransparentPtrType> std::iter::IntoIterator for List<T> {
type Item = T;
type IntoIter = IntoIter<T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
IntoIter::new(self)
}
}
impl<T: TransparentPtrType> FromGlibContainer<<T as GlibPtrDefault>::GlibType, *mut ffi::GList>
for List<T>
{
#[inline]
unsafe fn from_glib_none_num(ptr: *mut ffi::GList, _num: usize) -> Self {
Self::from_glib_none(ptr)
}
#[inline]
unsafe fn from_glib_container_num(ptr: *mut ffi::GList, _num: usize) -> Self {
Self::from_glib_container(ptr)
}
#[inline]
unsafe fn from_glib_full_num(ptr: *mut ffi::GList, _num: usize) -> Self {
Self::from_glib_full(ptr)
}
}
impl<T: TransparentPtrType> FromGlibContainer<<T as GlibPtrDefault>::GlibType, *const ffi::GList>
for List<T>
{
#[inline]
unsafe fn from_glib_none_num(ptr: *const ffi::GList, _num: usize) -> Self {
Self::from_glib_none(ptr)
}
unsafe fn from_glib_container_num(_ptr: *const ffi::GList, _num: usize) -> Self {
unimplemented!();
}
unsafe fn from_glib_full_num(_ptr: *const ffi::GList, _num: usize) -> Self {
unimplemented!();
}
}
impl<T: TransparentPtrType> FromGlibPtrContainer<<T as GlibPtrDefault>::GlibType, *mut ffi::GList>
for List<T>
{
#[inline]
unsafe fn from_glib_none(ptr: *mut ffi::GList) -> Self {
Self::from_glib_none(ptr)
}
#[inline]
unsafe fn from_glib_container(ptr: *mut ffi::GList) -> Self {
Self::from_glib_container(ptr)
}
#[inline]
unsafe fn from_glib_full(ptr: *mut ffi::GList) -> Self {
Self::from_glib_full(ptr)
}
}
impl<T: TransparentPtrType> FromGlibPtrContainer<<T as GlibPtrDefault>::GlibType, *const ffi::GList>
for List<T>
{
#[inline]
unsafe fn from_glib_none(ptr: *const ffi::GList) -> Self {
Self::from_glib_none(ptr)
}
unsafe fn from_glib_container(_ptr: *const ffi::GList) -> Self {
unimplemented!();
}
unsafe fn from_glib_full(_ptr: *const ffi::GList) -> Self {
unimplemented!();
}
}
impl<'a, T: TransparentPtrType + 'a> ToGlibPtr<'a, *mut ffi::GList> for List<T> {
type Storage = PhantomData<&'a Self>;
#[inline]
fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::GList, Self> {
Stash(self.as_ptr() as *mut _, PhantomData)
}
#[inline]
fn to_glib_container(&'a self) -> Stash<'a, *mut ffi::GList, Self> {
unsafe {
let ptr = ffi::g_malloc(mem::size_of::<T>().checked_mul(self.len() + 1).unwrap())
as *mut ffi::GList;
ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len() + 1);
Stash(ptr, PhantomData)
}
}
#[inline]
fn to_glib_full(&self) -> *mut ffi::GList {
self.clone().into_raw()
}
}
impl<'a, T: TransparentPtrType + 'a> ToGlibPtr<'a, *const ffi::GList> for List<T> {
type Storage = PhantomData<&'a Self>;
#[inline]
fn to_glib_none(&'a self) -> Stash<'a, *const ffi::GList, Self> {
Stash(self.as_ptr(), PhantomData)
}
}
impl<'a, T: TransparentPtrType + 'a> ToGlibPtrMut<'a, *mut ffi::GList> for List<T> {
type Storage = PhantomData<&'a mut Self>;
#[inline]
fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::GList, Self> {
StashMut(self.as_mut_ptr(), PhantomData)
}
}
impl<T: TransparentPtrType> IntoGlibPtr<*mut ffi::GList> for List<T> {
#[inline]
unsafe fn into_glib_ptr(self) -> *mut ffi::GList {
self.into_raw()
}
}
// rustdoc-stripper-ignore-next
/// A non-destructive iterator over a [`List`].
pub struct Iter<'a, T: TransparentPtrType> {
ptr: Option<ptr::NonNull<ffi::GList>>,
phantom: PhantomData<&'a T>,
}
impl<'a, T: TransparentPtrType> Iter<'a, T> {
#[inline]
fn new(list: &'a List<T>) -> Iter<'a, T> {
debug_assert_eq!(
mem::size_of::<T>(),
mem::size_of::<<T as GlibPtrDefault>::GlibType>()
);
Iter {
ptr: list.ptr,
phantom: PhantomData,
}
}
}
impl<'a, T: TransparentPtrType> Iterator for Iter<'a, T> {
type Item = &'a T;
#[inline]
fn next(&mut self) -> Option<&'a T> {
match self.ptr {
None => None,
Some(cur) => unsafe {
self.ptr = ptr::NonNull::new(cur.as_ref().next);
let item = &*(&cur.as_ref().data as *const ffi::gpointer as *const T);
Some(item)
},
}
}
}
impl<'a, T: TransparentPtrType> FusedIterator for Iter<'a, T> {}
// rustdoc-stripper-ignore-next
/// A non-destructive iterator over a [`List`].
pub struct IterMut<'a, T: TransparentPtrType> {
ptr: Option<ptr::NonNull<ffi::GList>>,
phantom: PhantomData<&'a mut T>,
}
impl<'a, T: TransparentPtrType> IterMut<'a, T> {
#[inline]
fn new(list: &'a mut List<T>) -> IterMut<'a, T> {
debug_assert_eq!(
mem::size_of::<T>(),
mem::size_of::<<T as GlibPtrDefault>::GlibType>()
);
IterMut {
ptr: list.ptr,
phantom: PhantomData,
}
}
}
impl<'a, T: TransparentPtrType> Iterator for IterMut<'a, T> {
type Item = &'a mut T;
#[inline]
fn next(&mut self) -> Option<&'a mut T> {
match self.ptr {
None => None,
Some(mut cur) => unsafe {
self.ptr = ptr::NonNull::new(cur.as_ref().next);
let item = &mut *(&mut cur.as_mut().data as *mut ffi::gpointer as *mut T);
Some(item)
},
}
}
}
impl<'a, T: TransparentPtrType> FusedIterator for IterMut<'a, T> {}
// rustdoc-stripper-ignore-next
/// A destructive iterator over a [`List`].
pub struct IntoIter<T: TransparentPtrType> {
list: List<T>,
}
impl<T: TransparentPtrType> IntoIter<T> {
#[inline]
fn new(list: List<T>) -> IntoIter<T> {
debug_assert_eq!(
mem::size_of::<T>(),
mem::size_of::<<T as GlibPtrDefault>::GlibType>()
);
IntoIter { list }
}
}
impl<T: TransparentPtrType> Iterator for IntoIter<T> {
type Item = T;
#[inline]
fn next(&mut self) -> Option<T> {
self.list.pop_front()
}
}
impl<T: TransparentPtrType> FusedIterator for IntoIter<T> {}
#[cfg(test)]
mod test {
use super::*;
#[test]
// checker-ignore-item
fn from_glib_full() {
let items = [
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
];
let mut list = unsafe {
let mut list = ffi::g_list_append(
ptr::null_mut(),
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[0]) as ffi::gpointer,
);
list = ffi::g_list_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[1]) as ffi::gpointer,
);
list = ffi::g_list_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[2]) as ffi::gpointer,
);
list = ffi::g_list_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[3]) as ffi::gpointer,
);
List::<crate::DateTime>::from_glib_full(list)
};
assert!(!list.is_empty());
let list_items = list.iter().cloned().collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list_items = list.iter_mut().map(|d| d.clone()).collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list_items = list.into_iter().collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list = unsafe { List::<crate::DateTime>::from_glib_full(ptr::null_mut()) };
assert!(list.is_empty());
}
#[test]
// checker-ignore-item
fn from_glib_container() {
let items = [
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
];
let mut list = unsafe {
let mut list = ffi::g_list_append(
ptr::null_mut(),
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[0]).0 as ffi::gpointer,
);
list = ffi::g_list_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[1]).0 as ffi::gpointer,
);
list = ffi::g_list_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[2]).0 as ffi::gpointer,
);
list = ffi::g_list_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[3]).0 as ffi::gpointer,
);
List::<crate::DateTime>::from_glib_container(list)
};
assert!(!list.is_empty());
let list_items = list.iter().cloned().collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list_items = list.iter_mut().map(|d| d.clone()).collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list_items = list.into_iter().collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list = unsafe { List::<crate::DateTime>::from_glib_full(ptr::null_mut()) };
assert!(list.is_empty());
}
#[test]
// checker-ignore-item
fn from_glib_none() {
let items = [
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
];
let mut list = unsafe {
let mut list = ffi::g_list_append(
ptr::null_mut(),
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[0]).0 as ffi::gpointer,
);
list = ffi::g_list_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[1]).0 as ffi::gpointer,
);
list = ffi::g_list_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[2]).0 as ffi::gpointer,
);
list = ffi::g_list_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[3]).0 as ffi::gpointer,
);
let res = List::<crate::DateTime>::from_glib_none(list);
ffi::g_list_free(list);
res
};
assert!(!list.is_empty());
let list_items = list.iter().cloned().collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list_items = list.iter_mut().map(|d| d.clone()).collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list_items = list.into_iter().collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list = unsafe { List::<crate::DateTime>::from_glib_full(ptr::null_mut()) };
assert!(list.is_empty());
}
#[test]
// checker-ignore-item
fn safe_api() {
let items = [
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
];
let mut list = items[1..3].iter().cloned().collect::<List<_>>();
assert_eq!(list.len(), 2);
list.push_front(items[0].clone());
assert_eq!(list.len(), 3);
list.push_back(items[3].clone());
assert_eq!(list.len(), 4);
let list_items = list.iter().cloned().collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
assert_eq!(list.front(), Some(&items[0]));
assert_eq!(list.back(), Some(&items[3]));
assert_eq!(list.pop_front().as_ref(), Some(&items[0]));
assert_eq!(list.len(), 3);
list.reverse();
let mut list_items = list.iter().cloned().collect::<Vec<_>>();
list_items.reverse();
assert_eq!(&items[1..], &list_items);
let list2 = list.clone();
let mut list_items = list2.iter().cloned().collect::<Vec<_>>();
list_items.reverse();
assert_eq!(&items[1..], &list_items);
}
}
@@ -0,0 +1,16 @@
// Take a look at the license at the top of the repository in the LICENSE file.
pub mod ptr_slice;
pub use ptr_slice::PtrSlice;
pub mod slice;
pub use slice::Slice;
pub mod list;
pub use list::List;
pub mod slist;
pub use slist::SList;
pub mod strv;
pub use strv::StrV;
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,991 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{fmt, marker::PhantomData, mem, ptr};
use crate::translate::*;
// rustdoc-stripper-ignore-next
/// Minimum size of the `Slice` allocation in bytes.
const MIN_SIZE: usize = 256;
// rustdoc-stripper-ignore-next
/// Slice of elements of type `T` allocated by the GLib allocator.
///
/// This can be used like a `&[T]`, `&mut [T]` and `Vec<T>`.
pub struct Slice<T: TransparentType> {
ptr: ptr::NonNull<T::GlibType>,
len: usize,
capacity: usize,
}
unsafe impl<T: TransparentType + Send> Send for Slice<T> {}
unsafe impl<T: TransparentType + Sync> Sync for Slice<T> {}
impl<T: fmt::Debug + TransparentType> fmt::Debug for Slice<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_slice().fmt(f)
}
}
impl<T: PartialEq + TransparentType> PartialEq for Slice<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}
impl<T: Eq + TransparentType> Eq for Slice<T> {}
impl<T: PartialOrd + TransparentType> PartialOrd for Slice<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.as_slice().partial_cmp(other.as_slice())
}
}
impl<T: Ord + TransparentType> Ord for Slice<T> {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_slice().cmp(other.as_slice())
}
}
impl<T: std::hash::Hash + TransparentType> std::hash::Hash for Slice<T> {
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_slice().hash(state)
}
}
impl<T: PartialEq + TransparentType> PartialEq<[T]> for Slice<T> {
#[inline]
fn eq(&self, other: &[T]) -> bool {
self.as_slice() == other
}
}
impl<T: PartialEq + TransparentType> PartialEq<Slice<T>> for [T] {
#[inline]
fn eq(&self, other: &Slice<T>) -> bool {
self == other.as_slice()
}
}
impl<T: TransparentType> Drop for Slice<T> {
#[inline]
fn drop(&mut self) {
unsafe {
if mem::needs_drop::<T>() {
for i in 0..self.len {
ptr::drop_in_place::<T>(self.ptr.as_ptr().add(i) as *mut T);
}
}
if self.capacity != 0 {
ffi::g_free(self.ptr.as_ptr() as ffi::gpointer);
}
}
}
}
impl<T: TransparentType> AsRef<[T]> for Slice<T> {
#[inline]
fn as_ref(&self) -> &[T] {
self.as_slice()
}
}
impl<T: TransparentType> AsMut<[T]> for Slice<T> {
#[inline]
fn as_mut(&mut self) -> &mut [T] {
self.as_mut_slice()
}
}
impl<T: TransparentType> std::borrow::Borrow<[T]> for Slice<T> {
#[inline]
fn borrow(&self) -> &[T] {
self.as_slice()
}
}
impl<T: TransparentType> std::borrow::BorrowMut<[T]> for Slice<T> {
#[inline]
fn borrow_mut(&mut self) -> &mut [T] {
self.as_mut_slice()
}
}
impl<T: TransparentType> std::ops::Deref for Slice<T> {
type Target = [T];
#[inline]
fn deref(&self) -> &[T] {
self.as_slice()
}
}
impl<T: TransparentType> std::ops::DerefMut for Slice<T> {
#[inline]
fn deref_mut(&mut self) -> &mut [T] {
self.as_mut_slice()
}
}
impl<T: TransparentType> Default for Slice<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T: TransparentType> std::iter::Extend<T> for Slice<T> {
#[inline]
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
let iter = iter.into_iter();
self.reserve(iter.size_hint().0);
for item in iter {
self.push(item);
}
}
}
impl<'a, T: TransparentType + 'a> std::iter::Extend<&'a T> for Slice<T> {
#[inline]
fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
let iter = iter.into_iter();
self.reserve(iter.size_hint().0);
for item in iter {
self.push(item.clone());
}
}
}
impl<T: TransparentType> std::iter::FromIterator<T> for Slice<T> {
#[inline]
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let iter = iter.into_iter();
let mut s = Self::with_capacity(iter.size_hint().0);
for item in iter {
s.push(item);
}
s
}
}
impl<'a, T: TransparentType> std::iter::IntoIterator for &'a Slice<T> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.as_slice().iter()
}
}
impl<'a, T: TransparentType> std::iter::IntoIterator for &'a mut Slice<T> {
type Item = &'a mut T;
type IntoIter = std::slice::IterMut<'a, T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.as_mut_slice().iter_mut()
}
}
impl<T: TransparentType> std::iter::IntoIterator for Slice<T> {
type Item = T;
type IntoIter = IntoIter<T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
IntoIter::new(self)
}
}
pub struct IntoIter<T: TransparentType> {
ptr: ptr::NonNull<T::GlibType>,
idx: ptr::NonNull<T::GlibType>,
len: usize,
empty: bool,
}
impl<T: TransparentType> IntoIter<T> {
#[inline]
fn new(slice: Slice<T>) -> Self {
let slice = mem::ManuallyDrop::new(slice);
IntoIter {
ptr: slice.ptr,
idx: slice.ptr,
len: slice.len,
empty: slice.capacity == 0,
}
}
// rustdoc-stripper-ignore-next
/// Returns the remaining items as slice.
#[inline]
pub fn as_slice(&self) -> &[T] {
unsafe {
if self.len == 0 {
&[]
} else {
std::slice::from_raw_parts(self.idx.as_ptr() as *mut T, self.len)
}
}
}
// rustdoc-stripper-ignore-next
/// Returns the remaining items as mutable slice.
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [T] {
unsafe {
if self.len == 0 {
&mut []
} else {
std::slice::from_raw_parts_mut(self.idx.as_ptr() as *mut T, self.len)
}
}
}
}
impl<T: TransparentType> Drop for IntoIter<T> {
#[inline]
fn drop(&mut self) {
unsafe {
if mem::needs_drop::<T>() {
for i in 0..self.len {
ptr::drop_in_place::<T>(self.idx.as_ptr().add(i) as *mut T);
}
}
if !self.empty {
ffi::g_free(self.ptr.as_ptr() as ffi::gpointer);
}
}
}
}
impl<T: TransparentType> Iterator for IntoIter<T> {
type Item = T;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.len == 0 {
return None;
}
unsafe {
let p = self.idx.as_ptr();
self.len -= 1;
self.idx = ptr::NonNull::new_unchecked(p.add(1));
Some(ptr::read(p as *mut T))
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
#[inline]
fn count(self) -> usize {
self.len
}
#[inline]
fn last(mut self) -> Option<T> {
if self.len == 0 {
None
} else {
self.len -= 1;
Some(unsafe { ptr::read(self.idx.as_ptr().add(self.len) as *mut T) })
}
}
}
impl<T: TransparentType> DoubleEndedIterator for IntoIter<T> {
#[inline]
fn next_back(&mut self) -> Option<T> {
if self.len == 0 {
None
} else {
self.len -= 1;
Some(unsafe { ptr::read(self.idx.as_ptr().add(self.len) as *mut T) })
}
}
}
impl<T: TransparentType> ExactSizeIterator for IntoIter<T> {}
impl<T: TransparentType> std::iter::FusedIterator for IntoIter<T> {}
impl<T: TransparentType> From<Slice<T>> for Vec<T> {
#[inline]
fn from(mut value: Slice<T>) -> Self {
unsafe {
let mut s = Vec::with_capacity(value.len);
ptr::copy_nonoverlapping(value.ptr.as_ptr() as *const T, s.as_mut_ptr(), value.len);
s.set_len(value.len);
value.len = 0;
s
}
}
}
impl<T: TransparentType> From<Vec<T>> for Slice<T> {
#[inline]
fn from(mut value: Vec<T>) -> Self {
unsafe {
let mut s = Self::with_capacity(value.len());
ptr::copy_nonoverlapping(value.as_ptr(), s.ptr.as_ptr() as *mut T, value.len());
s.len = value.len();
value.set_len(0);
s
}
}
}
impl<T: TransparentType, const N: usize> From<[T; N]> for Slice<T> {
#[inline]
fn from(value: [T; N]) -> Self {
unsafe {
let value = mem::ManuallyDrop::new(value);
let len = value.len();
let mut s = Self::with_capacity(len);
ptr::copy_nonoverlapping(value.as_ptr(), s.ptr.as_ptr() as *mut T, len);
s.len = len;
s
}
}
}
impl<'a, T: TransparentType> From<&'a [T]> for Slice<T> {
#[inline]
fn from(value: &'a [T]) -> Self {
unsafe {
let mut s = Self::with_capacity(value.len());
for (i, item) in value.iter().enumerate() {
ptr::write(s.ptr.as_ptr().add(i) as *mut T, item.clone());
}
s.len = value.len();
s
}
}
}
impl<'a, T: TransparentType> From<&'a [&'a T]> for Slice<T> {
#[inline]
fn from(value: &'a [&'a T]) -> Self {
unsafe {
let mut s = Self::with_capacity(value.len());
for (i, item) in value.iter().enumerate() {
ptr::write(s.ptr.as_ptr().add(i) as *mut T, (*item).clone());
}
s.len = value.len();
s
}
}
}
impl<T: TransparentType> Clone for Slice<T> {
#[inline]
fn clone(&self) -> Self {
Self::from(self.as_slice())
}
}
impl<T: TransparentType> Slice<T> {
// rustdoc-stripper-ignore-next
/// Borrows a C array.
#[inline]
pub unsafe fn from_glib_borrow_num<'a>(ptr: *const T::GlibType, len: usize) -> &'a [T] {
debug_assert!(!ptr.is_null() || len == 0);
if len == 0 {
&[]
} else {
std::slice::from_raw_parts(ptr as *const T, len)
}
}
// rustdoc-stripper-ignore-next
/// Borrows a mutable C array.
#[inline]
pub unsafe fn from_glib_borrow_num_mut<'a>(ptr: *mut T::GlibType, len: usize) -> &'a mut [T] {
debug_assert!(!ptr.is_null() || len == 0);
if len == 0 {
&mut []
} else {
std::slice::from_raw_parts_mut(ptr as *mut T, len)
}
}
// rustdoc-stripper-ignore-next
/// Borrows a C array of references.
#[inline]
pub unsafe fn from_glib_ptr_borrow_num<'a>(
ptr: *const *const T::GlibType,
len: usize,
) -> &'a [&'a T] {
debug_assert!(!ptr.is_null() || len == 0);
if len == 0 {
&[]
} else {
std::slice::from_raw_parts(ptr as *const &T, len)
}
}
// rustdoc-stripper-ignore-next
/// Borrows a mutable C array.
#[inline]
pub unsafe fn from_glib_ptr_borrow_num_mut<'a>(
ptr: *mut *mut T::GlibType,
len: usize,
) -> &'a mut [&'a mut T] {
debug_assert!(!ptr.is_null() || len == 0);
if len == 0 {
&mut []
} else {
std::slice::from_raw_parts_mut(ptr as *mut &mut T, len)
}
}
// rustdoc-stripper-ignore-next
/// Create a new `Slice` around a C array.
#[inline]
pub unsafe fn from_glib_none_num(ptr: *const T::GlibType, len: usize) -> Self {
debug_assert_eq!(mem::size_of::<T>(), mem::size_of::<T::GlibType>());
debug_assert!(!ptr.is_null() || len == 0);
if len == 0 {
Slice::default()
} else {
// Need to fully copy the array here.
let s = Self::from_glib_borrow_num(ptr, len);
Self::from(s)
}
}
// rustdoc-stripper-ignore-next
/// Create a new `Slice` around a C array.
#[inline]
pub unsafe fn from_glib_container_num(ptr: *mut T::GlibType, len: usize) -> Self {
debug_assert_eq!(mem::size_of::<T>(), mem::size_of::<T::GlibType>());
debug_assert!(!ptr.is_null() || len == 0);
if len == 0 {
ffi::g_free(ptr as ffi::gpointer);
Slice::default()
} else {
// Need to clone every item because we don't own it here but only
// if this type requires explicit drop.
if mem::needs_drop::<T>() {
for i in 0..len {
let p = ptr.add(i) as *mut T;
let clone: T = (*p).clone();
ptr::write(p, clone);
}
}
// And now it can be handled exactly the same as `from_glib_full_num()`.
Self::from_glib_full_num(ptr, len)
}
}
// rustdoc-stripper-ignore-next
/// Create a new `Slice` around a C array.
#[inline]
pub unsafe fn from_glib_full_num(ptr: *mut T::GlibType, len: usize) -> Self {
debug_assert_eq!(mem::size_of::<T>(), mem::size_of::<T::GlibType>());
debug_assert!(!ptr.is_null() || len == 0);
if len == 0 {
ffi::g_free(ptr as ffi::gpointer);
Slice::default()
} else {
Slice {
ptr: ptr::NonNull::new_unchecked(ptr),
len,
capacity: len,
}
}
}
// rustdoc-stripper-ignore-next
/// Creates a new empty slice.
#[inline]
pub fn new() -> Self {
Slice {
ptr: ptr::NonNull::dangling(),
len: 0,
capacity: 0,
}
}
// rustdoc-stripper-ignore-next
/// Creates a new empty slice with the given capacity.
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
let mut s = Self::new();
s.reserve(capacity);
s
}
// rustdoc-stripper-ignore-next
/// Returns the underlying pointer.
///
/// This is guaranteed to be `NULL`-terminated.
#[inline]
pub fn as_ptr(&self) -> *const T::GlibType {
if self.len == 0 {
ptr::null()
} else {
self.ptr.as_ptr()
}
}
// rustdoc-stripper-ignore-next
/// Returns the underlying pointer.
///
/// This is guaranteed to be `NULL`-terminated.
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut T::GlibType {
if self.len == 0 {
ptr::null_mut()
} else {
self.ptr.as_ptr()
}
}
// rustdoc-stripper-ignore-next
/// Consumes the slice and returns the underlying pointer.
///
/// This is guaranteed to be `NULL`-terminated.
#[inline]
pub fn into_raw(mut self) -> *mut T::GlibType {
if self.len == 0 {
ptr::null_mut()
} else {
self.len = 0;
self.capacity = 0;
self.ptr.as_ptr()
}
}
// rustdoc-stripper-ignore-next
/// Gets the length of the slice.
#[inline]
pub fn len(&self) -> usize {
self.len
}
// rustdoc-stripper-ignore-next
/// Returns `true` if the slice is empty.
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
// rustdoc-stripper-ignore-next
/// Returns the capacity of the slice.
#[inline]
pub fn capacity(&self) -> usize {
self.capacity
}
// rustdoc-stripper-ignore-next
/// Sets the length of the slice to `len`.
///
/// # SAFETY
///
/// There must be at least `len` valid items.
pub unsafe fn set_len(&mut self, len: usize) {
self.len = len;
}
// rustdoc-stripper-ignore-next
/// Reserves at least this much additional capacity.
pub fn reserve(&mut self, additional: usize) {
// Nothing new to reserve as there's still enough space
if self.len + additional <= self.capacity {
return;
}
let new_capacity = usize::next_power_of_two(std::cmp::max(
self.len + additional,
MIN_SIZE / mem::size_of::<T>(),
));
assert_ne!(new_capacity, 0);
assert!(new_capacity > self.capacity);
unsafe {
let ptr = if self.capacity == 0 {
ptr::null_mut()
} else {
self.ptr.as_ptr() as *mut _
};
let new_ptr =
ffi::g_realloc(ptr, mem::size_of::<T>().checked_mul(new_capacity).unwrap())
as *mut T::GlibType;
self.ptr = ptr::NonNull::new_unchecked(new_ptr);
self.capacity = new_capacity;
}
}
// rustdoc-stripper-ignore-next
/// Borrows this slice as a `&[T]`.
#[inline]
pub fn as_slice(&self) -> &[T] {
unsafe {
if self.len == 0 {
&[]
} else {
std::slice::from_raw_parts(self.ptr.as_ptr() as *const T, self.len)
}
}
}
// rustdoc-stripper-ignore-next
/// Borrows this slice as a `&mut [T]`.
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [T] {
unsafe {
if self.len == 0 {
&mut []
} else {
std::slice::from_raw_parts_mut(self.ptr.as_ptr() as *mut T, self.len)
}
}
}
// rustdoc-stripper-ignore-next
/// Removes all items from the slice.
#[inline]
pub fn clear(&mut self) {
unsafe {
if mem::needs_drop::<T>() {
for i in 0..self.len {
ptr::drop_in_place::<T>(self.ptr.as_ptr().add(i) as *mut T);
}
}
self.len = 0;
}
}
// rustdoc-stripper-ignore-next
/// Clones and appends all elements in `slice` to the slice.
#[inline]
pub fn extend_from_slice(&mut self, other: &[T]) {
// Nothing new to reserve as there's still enough space
if self.len + other.len() > self.capacity {
self.reserve(other.len());
}
unsafe {
for item in other {
ptr::write(self.ptr.as_ptr().add(self.len) as *mut T, item.clone());
self.len += 1;
}
}
}
// rustdoc-stripper-ignore-next
/// Inserts `item` at position `index` of the slice, shifting all elements after it to the
/// right.
#[inline]
#[allow(clippy::int_plus_one)]
pub fn insert(&mut self, index: usize, item: T) {
assert!(index <= self.len);
// Nothing new to reserve as there's still enough space
if self.len + 1 > self.capacity {
self.reserve(1);
}
unsafe {
if index == self.len {
ptr::write(self.ptr.as_ptr().add(self.len) as *mut T, item);
} else {
let p = self.ptr.as_ptr().add(index);
ptr::copy(p, p.add(1), self.len - index);
ptr::write(self.ptr.as_ptr().add(index) as *mut T, item);
}
self.len += 1;
}
}
// rustdoc-stripper-ignore-next
/// Pushes `item` to the end of the slice.
#[inline]
#[allow(clippy::int_plus_one)]
pub fn push(&mut self, item: T) {
// Nothing new to reserve as there's still enough space
if self.len + 1 > self.capacity {
self.reserve(1);
}
unsafe {
ptr::write(self.ptr.as_ptr().add(self.len) as *mut T, item);
self.len += 1;
}
}
// rustdoc-stripper-ignore-next
/// Removes item from position `index` of the slice, shifting all elements after it to the
/// left.
#[inline]
pub fn remove(&mut self, index: usize) -> T {
assert!(index < self.len);
unsafe {
let p = self.ptr.as_ptr().add(index);
let item = ptr::read(p as *mut T);
ptr::copy(p.add(1), p, self.len - index - 1);
self.len -= 1;
item
}
}
// rustdoc-stripper-ignore-next
/// Removes the last item of the slice and returns it.
#[inline]
pub fn pop(&mut self) -> Option<T> {
if self.len == 0 {
return None;
}
unsafe {
self.len -= 1;
let p = self.ptr.as_ptr().add(self.len);
let item = ptr::read(p as *mut T);
Some(item)
}
}
// rustdoc-stripper-ignore-next
/// Shortens the slice by keeping the last `len` items.
///
/// If there are fewer than `len` items then this has no effect.
#[inline]
pub fn truncate(&mut self, len: usize) {
if self.len <= len {
return;
}
unsafe {
while self.len > len {
self.len -= 1;
let p = self.ptr.as_ptr().add(self.len);
ptr::drop_in_place::<T>(p as *mut T);
}
}
}
}
impl<T: TransparentType + 'static> FromGlibContainer<T::GlibType, *mut T::GlibType> for Slice<T> {
unsafe fn from_glib_none_num(ptr: *mut T::GlibType, num: usize) -> Self {
Self::from_glib_none_num(ptr, num)
}
#[inline]
unsafe fn from_glib_container_num(ptr: *mut T::GlibType, num: usize) -> Self {
Self::from_glib_container_num(ptr, num)
}
#[inline]
unsafe fn from_glib_full_num(ptr: *mut T::GlibType, num: usize) -> Self {
Self::from_glib_full_num(ptr, num)
}
}
impl<T: TransparentType + 'static> FromGlibContainer<T::GlibType, *const T::GlibType> for Slice<T> {
unsafe fn from_glib_none_num(ptr: *const T::GlibType, num: usize) -> Self {
Self::from_glib_none_num(ptr, num)
}
unsafe fn from_glib_container_num(_ptr: *const T::GlibType, _num: usize) -> Self {
unimplemented!();
}
unsafe fn from_glib_full_num(_ptr: *const T::GlibType, _num: usize) -> Self {
unimplemented!();
}
}
impl<'a, T: TransparentType + 'a> ToGlibPtr<'a, *mut T::GlibType> for Slice<T> {
type Storage = PhantomData<&'a Self>;
#[inline]
fn to_glib_none(&'a self) -> Stash<'a, *mut T::GlibType, Self> {
Stash(self.as_ptr() as *mut _, PhantomData)
}
#[inline]
fn to_glib_container(&'a self) -> Stash<'a, *mut T::GlibType, Self> {
unsafe {
let ptr = ffi::g_malloc(mem::size_of::<T>().checked_mul(self.len()).unwrap())
as *mut T::GlibType;
ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len());
Stash(ptr, PhantomData)
}
}
#[inline]
fn to_glib_full(&self) -> *mut T::GlibType {
self.clone().into_raw()
}
}
impl<'a, T: TransparentType + 'a> ToGlibPtr<'a, *const T::GlibType> for Slice<T> {
type Storage = PhantomData<&'a Self>;
#[inline]
fn to_glib_none(&'a self) -> Stash<'a, *const T::GlibType, Self> {
Stash(self.as_ptr(), PhantomData)
}
}
impl<'a, T: TransparentType + 'a> ToGlibPtrMut<'a, *mut T::GlibType> for Slice<T> {
type Storage = PhantomData<&'a mut Self>;
#[inline]
fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut T::GlibType, Self> {
StashMut(self.as_mut_ptr(), PhantomData)
}
}
impl<T: TransparentType + 'static> IntoGlibPtr<*mut T::GlibType> for Slice<T> {
#[inline]
unsafe fn into_glib_ptr(self) -> *mut T::GlibType {
self.into_raw()
}
}
impl<T: TransparentPtrType> From<super::PtrSlice<T>> for Slice<T> {
fn from(value: super::PtrSlice<T>) -> Self {
let len = value.len();
let capacity = value.capacity();
unsafe {
let ptr = value.into_raw();
Slice::<T> {
ptr: ptr::NonNull::new_unchecked(ptr),
len,
capacity,
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_from_glib_full() {
let items = [
crate::Date::from_dmy(20, crate::DateMonth::November, 2021).unwrap(),
crate::Date::from_dmy(21, crate::DateMonth::November, 2021).unwrap(),
crate::Date::from_dmy(22, crate::DateMonth::November, 2021).unwrap(),
crate::Date::from_dmy(23, crate::DateMonth::November, 2021).unwrap(),
];
let slice = unsafe {
let ptr = ffi::g_malloc(mem::size_of::<ffi::GDate>() * 4) as *mut ffi::GDate;
ptr::write(ptr.add(0), *items[0].to_glib_none().0);
ptr::write(ptr.add(1), *items[1].to_glib_none().0);
ptr::write(ptr.add(2), *items[2].to_glib_none().0);
ptr::write(ptr.add(3), *items[3].to_glib_none().0);
Slice::<crate::Date>::from_glib_full_num(ptr, 4)
};
assert_eq!(&items[..], &*slice);
}
#[test]
fn test_from_glib_none() {
let items = [
crate::Date::from_dmy(20, crate::DateMonth::November, 2021).unwrap(),
crate::Date::from_dmy(21, crate::DateMonth::November, 2021).unwrap(),
crate::Date::from_dmy(22, crate::DateMonth::November, 2021).unwrap(),
crate::Date::from_dmy(23, crate::DateMonth::November, 2021).unwrap(),
];
let slice = unsafe {
Slice::<crate::Date>::from_glib_none_num(items.as_ptr() as *const ffi::GDate, 4)
};
assert_eq!(&items[..], &*slice);
}
#[test]
fn test_safe_api() {
let items = [
crate::Date::from_dmy(20, crate::DateMonth::November, 2021).unwrap(),
crate::Date::from_dmy(21, crate::DateMonth::November, 2021).unwrap(),
crate::Date::from_dmy(22, crate::DateMonth::November, 2021).unwrap(),
];
let mut slice = Slice::from(&items[..]);
assert_eq!(slice.len(), 3);
slice.push(crate::Date::from_dmy(23, crate::DateMonth::November, 2021).unwrap());
assert_eq!(slice.len(), 4);
for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
assert_eq!(a, b);
}
assert_eq!(
(slice[3].day(), slice[3].month(), slice[3].year()),
(23, crate::DateMonth::November, 2021)
);
let vec = Vec::from(slice);
assert_eq!(vec.len(), 4);
for (a, b) in Iterator::zip(items.iter(), vec.iter()) {
assert_eq!(a, b);
}
assert_eq!(
(vec[3].day(), vec[3].month(), vec[3].year()),
(23, crate::DateMonth::November, 2021)
);
let mut slice = Slice::from(vec);
assert_eq!(slice.len(), 4);
let e = slice.pop().unwrap();
assert_eq!(
(e.day(), e.month(), e.year()),
(23, crate::DateMonth::November, 2021)
);
assert_eq!(slice.len(), 3);
slice.insert(2, e);
assert_eq!(slice.len(), 4);
assert_eq!(slice[0].day(), 20);
assert_eq!(slice[1].day(), 21);
assert_eq!(slice[2].day(), 23);
assert_eq!(slice[3].day(), 22);
let e = slice.remove(2);
assert_eq!(
(e.day(), e.month(), e.year()),
(23, crate::DateMonth::November, 2021)
);
assert_eq!(slice.len(), 3);
slice.push(e);
assert_eq!(slice.len(), 4);
for (a, b) in Iterator::zip(items.iter(), slice.into_iter()) {
assert_eq!(a, &b);
}
}
}
@@ -0,0 +1,887 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{iter::FusedIterator, marker::PhantomData, mem, ptr};
use crate::translate::*;
// rustdoc-stripper-ignore-next
/// A list of items of type `T`.
///
/// Behaves like an `Iterator<Item = T>` but allows modifications.
#[repr(transparent)]
pub struct SList<T: TransparentPtrType> {
ptr: Option<ptr::NonNull<ffi::GSList>>,
phantom: PhantomData<T>,
}
#[doc(hidden)]
unsafe impl<T: TransparentPtrType> TransparentPtrType for SList<T> {}
#[doc(hidden)]
impl<T: TransparentPtrType> GlibPtrDefault for SList<T> {
type GlibType = *mut ffi::GSList;
}
unsafe impl<T: Send + TransparentPtrType> Send for SList<T> {}
unsafe impl<T: Sync + TransparentPtrType> Sync for SList<T> {}
impl<T: TransparentPtrType> SList<T> {
// rustdoc-stripper-ignore-next
/// Create a new `SList` around a list.
#[inline]
pub unsafe fn from_glib_none(list: *const ffi::GSList) -> SList<T> {
// Need to copy the whole list
let list = if mem::needs_drop::<T>() {
unsafe extern "C" fn copy_item<T: TransparentPtrType>(
ptr: ffi::gconstpointer,
_user_data: ffi::gpointer,
) -> ffi::gpointer {
let mut item = mem::ManuallyDrop::new(
(*(&ptr as *const ffi::gconstpointer as *const T)).clone(),
);
*(&mut *item as *mut T as *mut *mut T::GlibType) as ffi::gpointer
}
ffi::g_slist_copy_deep(mut_override(list), Some(copy_item::<T>), ptr::null_mut())
} else {
ffi::g_slist_copy(mut_override(list))
};
SList {
ptr: ptr::NonNull::new(list),
phantom: PhantomData,
}
}
// rustdoc-stripper-ignore-next
/// Create a new `SList` around a list.
#[inline]
pub unsafe fn from_glib_container(list: *mut ffi::GSList) -> SList<T> {
// Need to copy all items as we only own the container
if mem::needs_drop::<T>() {
unsafe extern "C" fn copy_item<T: TransparentPtrType>(
ptr: ffi::gpointer,
_user_data: ffi::gpointer,
) {
let item = (*(&ptr as *const ffi::gpointer as *const T)).clone();
ptr::write(ptr as *mut T, item);
}
ffi::g_slist_foreach(list, Some(copy_item::<T>), ptr::null_mut());
}
SList {
ptr: ptr::NonNull::new(list),
phantom: PhantomData,
}
}
// rustdoc-stripper-ignore-next
/// Create a new `SList` around a list.
#[inline]
pub unsafe fn from_glib_full(list: *mut ffi::GSList) -> SList<T> {
SList {
ptr: ptr::NonNull::new(list),
phantom: PhantomData,
}
}
// rustdoc-stripper-ignore-next
/// Creates a new empty list.
#[inline]
pub fn new() -> Self {
SList {
ptr: None,
phantom: PhantomData,
}
}
// rustdoc-stripper-ignore-next
/// Create a non-destructive iterator over the `SList`.
#[inline]
pub fn iter(&self) -> Iter<T> {
Iter::new(self)
}
// rustdoc-stripper-ignore-next
/// Create a non-destructive mutable iterator over the `SList`.
#[inline]
pub fn iter_mut(&mut self) -> IterMut<T> {
IterMut::new(self)
}
// rustdoc-stripper-ignore-next
/// Check if the list is empty.
///
/// This operation is `O(1)`.
#[inline]
pub fn is_empty(&self) -> bool {
self.ptr.is_none()
}
// rustdoc-stripper-ignore-next
/// Returns the length of the list.
///
/// This operation is `O(n)`.
#[inline]
#[doc(alias = "g_slist_length")]
pub fn len(&self) -> usize {
self.iter().count()
}
// rustdoc-stripper-ignore-next
/// Returns a reference to the first item of the list, if any.
///
/// This operation is `O(1)`.
#[inline]
#[doc(alias = "g_slist_first")]
pub fn front(&self) -> Option<&T> {
match self.ptr {
None => None,
Some(cur) => unsafe {
let item = &*(&cur.as_ref().data as *const ffi::gpointer as *const T);
Some(item)
},
}
}
// rustdoc-stripper-ignore-next
/// Returns a mutable reference to the first item of the list, if any.
///
/// This operation is `O(1)`.
#[inline]
#[doc(alias = "g_slist_first")]
pub fn front_mut(&mut self) -> Option<&mut T> {
match self.ptr {
None => None,
Some(mut cur) => unsafe {
let item = &mut *(&mut cur.as_mut().data as *mut ffi::gpointer as *mut T);
Some(item)
},
}
}
// rustdoc-stripper-ignore-next
/// Removes the front item from the list, if any.
///
/// This operation is `O(1)`.
#[inline]
pub fn pop_front(&mut self) -> Option<T> {
match self.ptr {
None => None,
Some(mut cur) => unsafe {
self.ptr = ptr::NonNull::new(cur.as_ref().next);
let item = ptr::read(&mut cur.as_mut().data as *mut ffi::gpointer as *mut T);
ffi::g_slist_free_1(cur.as_ptr());
Some(item)
},
}
}
// rustdoc-stripper-ignore-next
/// Prepends the new item to the front of the list.
///
/// This operation is `O(1)`.
#[inline]
#[doc(alias = "g_slist_prepend")]
pub fn push_front(&mut self, item: T) {
unsafe {
let ptr = self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut());
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_slist_prepend(
ptr,
*(&mut *mem::ManuallyDrop::new(item) as *mut T as *mut *mut T::GlibType)
as ffi::gpointer,
)));
}
}
// rustdoc-stripper-ignore-next
/// Returns a reference to the last item of the list, if any.
///
/// This operation is `O(n)`.
#[inline]
#[doc(alias = "g_slist_last")]
pub fn back(&self) -> Option<&T> {
unsafe {
let ptr = match self.ptr {
None => return None,
Some(ptr) => ptr.as_ptr(),
};
let last_ptr = ffi::g_slist_last(ptr);
let item = &*(&(*last_ptr).data as *const ffi::gpointer as *const T);
Some(item)
}
}
// rustdoc-stripper-ignore-next
/// Returns a mutable reference to the last item of the list, if any.
///
/// This operation is `O(n)`.
#[inline]
#[doc(alias = "g_slist_last")]
pub fn back_mut(&mut self) -> Option<&mut T> {
unsafe {
let ptr = match self.ptr {
None => return None,
Some(ptr) => ptr.as_ptr(),
};
let last_ptr = ffi::g_slist_last(ptr);
let item = &mut *(&mut (*last_ptr).data as *mut ffi::gpointer as *mut T);
Some(item)
}
}
// rustdoc-stripper-ignore-next
/// Removes the back item from the list, if any.
///
/// This operation is `O(n)`.
#[inline]
pub fn pop_back(&mut self) -> Option<T> {
unsafe {
let ptr = match self.ptr {
None => return None,
Some(ptr) => ptr.as_ptr(),
};
let last_ptr = ffi::g_slist_last(ptr);
let item = ptr::read(&mut (*last_ptr).data as *mut ffi::gpointer as *mut T);
self.ptr = ptr::NonNull::new(ffi::g_slist_delete_link(ptr, last_ptr));
Some(item)
}
}
// rustdoc-stripper-ignore-next
/// Appends the new item to the back of the list.
///
/// this operation is `O(n)`.
#[inline]
#[doc(alias = "g_slist_append")]
pub fn push_back(&mut self, item: T) {
unsafe {
let ptr = self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut());
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_slist_append(
ptr,
*(&mut *mem::ManuallyDrop::new(item) as *mut T as *mut *mut T::GlibType)
as ffi::gpointer,
)));
}
}
// rustdoc-stripper-ignore-next
/// Reverse the list.
///
/// This operation is `O(n)`.
#[inline]
#[doc(alias = "g_slist_reverse")]
pub fn reverse(&mut self) {
unsafe {
let ptr = match self.ptr {
None => return,
Some(ptr) => ptr.as_ptr(),
};
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_slist_reverse(ptr)));
}
}
// rustdoc-stripper-ignore-next
/// Sorts the list.
///
/// This operation is `O(n * log n)`.
#[inline]
#[doc(alias = "g_slist_sort")]
pub fn sort(&mut self)
where
T: Ord,
{
self.sort_by(|a, b| a.cmp(b));
}
// rustdoc-stripper-ignore-next
/// Sorts the list.
///
/// This operation is `O(n * log n)`.
#[inline]
#[doc(alias = "g_slist_sort")]
pub fn sort_by<F: FnMut(&T, &T) -> std::cmp::Ordering>(&mut self, mut f: F) {
unsafe {
let ptr = match self.ptr {
None => return,
Some(ptr) => ptr.as_ptr(),
};
unsafe extern "C" fn func<
T: TransparentPtrType,
F: FnMut(&T, &T) -> std::cmp::Ordering,
>(
a: ffi::gconstpointer,
b: ffi::gconstpointer,
user_data: ffi::gpointer,
) -> i32 {
let f = &mut *(user_data as *mut F);
let a = &*(&a as *const ffi::gconstpointer as *const T);
let b = &*(&b as *const ffi::gconstpointer as *const T);
f(a, b).into_glib()
}
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_slist_sort_with_data(
ptr,
Some(func::<T, F>),
&mut f as *mut F as ffi::gpointer,
)));
}
}
// rustdoc-stripper-ignore-next
/// Removes all items from the list.
#[inline]
pub fn clear(&mut self) {
*self = Self::new();
}
// rustdoc-stripper-ignore-next
/// Only keeps the item in the list for which `f` returns `true`.
#[inline]
pub fn retain(&mut self, mut f: impl FnMut(&T) -> bool) {
if let Some(head) = self.ptr {
unsafe {
let mut ptr = head.as_ptr();
while !ptr.is_null() {
let item = &*((*ptr).data as *const ffi::gpointer as *const T);
let next = (*ptr).next;
if !f(item) {
ptr::drop_in_place(&mut (*ptr).data as *mut ffi::gpointer as *mut T);
self.ptr = ptr::NonNull::new(ffi::g_slist_delete_link(head.as_ptr(), ptr));
}
ptr = next;
}
}
}
}
// rustdoc-stripper-ignore-next
/// Returns the underlying pointer.
#[inline]
pub fn as_ptr(&self) -> *const ffi::GSList {
self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut())
}
// rustdoc-stripper-ignore-next
/// Returns the underlying pointer.
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut ffi::GSList {
self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut())
}
// rustdoc-stripper-ignore-next
/// Consumes the list and returns the underlying pointer.
#[inline]
pub fn into_raw(mut self) -> *mut ffi::GSList {
self.ptr
.take()
.map(|p| p.as_ptr())
.unwrap_or(ptr::null_mut())
}
}
impl<T: TransparentPtrType> Default for SList<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: TransparentPtrType> Clone for SList<T> {
fn clone(&self) -> Self {
unsafe { Self::from_glib_none(self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut())) }
}
}
impl<T: TransparentPtrType> Drop for SList<T> {
#[inline]
fn drop(&mut self) {
if let Some(ptr) = self.ptr {
unsafe {
if mem::needs_drop::<T>() {
unsafe extern "C" fn drop_item<T: TransparentPtrType>(mut ptr: ffi::gpointer) {
ptr::drop_in_place(&mut ptr as *mut ffi::gpointer as *mut T);
}
ffi::g_slist_free_full(ptr.as_ptr(), Some(drop_item::<T>));
} else {
ffi::g_slist_free(ptr.as_ptr());
}
}
}
}
}
impl<T: TransparentPtrType> std::iter::FromIterator<T> for SList<T> {
#[inline]
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
unsafe {
let mut iter = iter.into_iter();
let first = match iter.next() {
None => return Self::new(),
Some(first) => first,
};
let list = ffi::g_slist_prepend(
ptr::null_mut(),
*(&mut *mem::ManuallyDrop::new(first) as *mut T as *mut *mut T::GlibType)
as ffi::gpointer,
);
let mut tail = list;
for item in iter {
let new_tail = ffi::g_slist_alloc();
(*new_tail).data = *(&mut *mem::ManuallyDrop::new(item) as *mut T
as *mut *mut T::GlibType) as ffi::gpointer;
(*new_tail).next = ptr::null_mut();
(*tail).next = new_tail;
tail = new_tail;
}
Self::from_glib_full(list)
}
}
}
impl<'a, T: TransparentPtrType> std::iter::IntoIterator for &'a SList<T> {
type Item = &'a T;
type IntoIter = Iter<'a, T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, T: TransparentPtrType> std::iter::IntoIterator for &'a mut SList<T> {
type Item = &'a mut T;
type IntoIter = IterMut<'a, T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
impl<T: TransparentPtrType> std::iter::IntoIterator for SList<T> {
type Item = T;
type IntoIter = IntoIter<T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
IntoIter::new(self)
}
}
impl<T: TransparentPtrType> FromGlibContainer<<T as GlibPtrDefault>::GlibType, *mut ffi::GSList>
for SList<T>
{
#[inline]
unsafe fn from_glib_none_num(ptr: *mut ffi::GSList, _num: usize) -> Self {
Self::from_glib_none(ptr)
}
#[inline]
unsafe fn from_glib_container_num(ptr: *mut ffi::GSList, _num: usize) -> Self {
Self::from_glib_container(ptr)
}
#[inline]
unsafe fn from_glib_full_num(ptr: *mut ffi::GSList, _num: usize) -> Self {
Self::from_glib_full(ptr)
}
}
impl<T: TransparentPtrType> FromGlibContainer<<T as GlibPtrDefault>::GlibType, *const ffi::GSList>
for SList<T>
{
#[inline]
unsafe fn from_glib_none_num(ptr: *const ffi::GSList, _num: usize) -> Self {
Self::from_glib_none(ptr)
}
unsafe fn from_glib_container_num(_ptr: *const ffi::GSList, _num: usize) -> Self {
unimplemented!();
}
unsafe fn from_glib_full_num(_ptr: *const ffi::GSList, _num: usize) -> Self {
unimplemented!();
}
}
impl<T: TransparentPtrType> FromGlibPtrContainer<<T as GlibPtrDefault>::GlibType, *mut ffi::GSList>
for SList<T>
{
#[inline]
unsafe fn from_glib_none(ptr: *mut ffi::GSList) -> Self {
Self::from_glib_none(ptr)
}
#[inline]
unsafe fn from_glib_container(ptr: *mut ffi::GSList) -> Self {
Self::from_glib_container(ptr)
}
#[inline]
unsafe fn from_glib_full(ptr: *mut ffi::GSList) -> Self {
Self::from_glib_full(ptr)
}
}
impl<T: TransparentPtrType>
FromGlibPtrContainer<<T as GlibPtrDefault>::GlibType, *const ffi::GSList> for SList<T>
{
#[inline]
unsafe fn from_glib_none(ptr: *const ffi::GSList) -> Self {
Self::from_glib_none(ptr)
}
unsafe fn from_glib_container(_ptr: *const ffi::GSList) -> Self {
unimplemented!();
}
unsafe fn from_glib_full(_ptr: *const ffi::GSList) -> Self {
unimplemented!();
}
}
impl<'a, T: TransparentPtrType + 'a> ToGlibPtr<'a, *mut ffi::GSList> for SList<T> {
type Storage = PhantomData<&'a Self>;
#[inline]
fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::GSList, Self> {
Stash(self.as_ptr() as *mut _, PhantomData)
}
#[inline]
fn to_glib_container(&'a self) -> Stash<'a, *mut ffi::GSList, Self> {
unsafe {
let ptr = ffi::g_malloc(mem::size_of::<T>().checked_mul(self.len() + 1).unwrap())
as *mut ffi::GSList;
ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len() + 1);
Stash(ptr, PhantomData)
}
}
#[inline]
fn to_glib_full(&self) -> *mut ffi::GSList {
self.clone().into_raw()
}
}
impl<'a, T: TransparentPtrType + 'a> ToGlibPtr<'a, *const ffi::GSList> for SList<T> {
type Storage = PhantomData<&'a Self>;
#[inline]
fn to_glib_none(&'a self) -> Stash<'a, *const ffi::GSList, Self> {
Stash(self.as_ptr(), PhantomData)
}
}
impl<'a, T: TransparentPtrType + 'a> ToGlibPtrMut<'a, *mut ffi::GSList> for SList<T> {
type Storage = PhantomData<&'a mut Self>;
#[inline]
fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::GSList, Self> {
StashMut(self.as_mut_ptr(), PhantomData)
}
}
impl<T: TransparentPtrType> IntoGlibPtr<*mut ffi::GSList> for SList<T> {
#[inline]
unsafe fn into_glib_ptr(self) -> *mut ffi::GSList {
self.into_raw()
}
}
// rustdoc-stripper-ignore-next
/// A non-destructive iterator over a [`SList`].
pub struct Iter<'a, T: TransparentPtrType> {
ptr: Option<ptr::NonNull<ffi::GSList>>,
phantom: PhantomData<&'a T>,
}
impl<'a, T: TransparentPtrType> Iter<'a, T> {
#[inline]
fn new(list: &'a SList<T>) -> Iter<'a, T> {
debug_assert_eq!(
mem::size_of::<T>(),
mem::size_of::<<T as GlibPtrDefault>::GlibType>()
);
Iter {
ptr: list.ptr,
phantom: PhantomData,
}
}
}
impl<'a, T: TransparentPtrType> Iterator for Iter<'a, T> {
type Item = &'a T;
#[inline]
fn next(&mut self) -> Option<&'a T> {
match self.ptr {
None => None,
Some(cur) => unsafe {
self.ptr = ptr::NonNull::new(cur.as_ref().next);
let item = &*(&cur.as_ref().data as *const ffi::gpointer as *const T);
Some(item)
},
}
}
}
impl<'a, T: TransparentPtrType> FusedIterator for Iter<'a, T> {}
// rustdoc-stripper-ignore-next
/// A non-destructive iterator over a [`SList`].
pub struct IterMut<'a, T: TransparentPtrType> {
ptr: Option<ptr::NonNull<ffi::GSList>>,
phantom: PhantomData<&'a mut T>,
}
impl<'a, T: TransparentPtrType> IterMut<'a, T> {
#[inline]
fn new(list: &'a mut SList<T>) -> IterMut<'a, T> {
debug_assert_eq!(
mem::size_of::<T>(),
mem::size_of::<<T as GlibPtrDefault>::GlibType>()
);
IterMut {
ptr: list.ptr,
phantom: PhantomData,
}
}
}
impl<'a, T: TransparentPtrType> Iterator for IterMut<'a, T> {
type Item = &'a mut T;
#[inline]
fn next(&mut self) -> Option<&'a mut T> {
match self.ptr {
None => None,
Some(mut cur) => unsafe {
self.ptr = ptr::NonNull::new(cur.as_ref().next);
let item = &mut *(&mut cur.as_mut().data as *mut ffi::gpointer as *mut T);
Some(item)
},
}
}
}
impl<'a, T: TransparentPtrType> FusedIterator for IterMut<'a, T> {}
// rustdoc-stripper-ignore-next
/// A destructive iterator over a [`SList`].
pub struct IntoIter<T: TransparentPtrType> {
list: SList<T>,
}
impl<T: TransparentPtrType> IntoIter<T> {
#[inline]
fn new(list: SList<T>) -> IntoIter<T> {
debug_assert_eq!(
mem::size_of::<T>(),
mem::size_of::<<T as GlibPtrDefault>::GlibType>()
);
IntoIter { list }
}
}
impl<T: TransparentPtrType> Iterator for IntoIter<T> {
type Item = T;
#[inline]
fn next(&mut self) -> Option<T> {
self.list.pop_front()
}
}
impl<T: TransparentPtrType> FusedIterator for IntoIter<T> {}
#[cfg(test)]
mod test {
use super::*;
#[test]
// checker-ignore-item
fn from_glib_full() {
let items = [
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
];
let mut list = unsafe {
let mut list = ffi::g_slist_append(
ptr::null_mut(),
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[0]) as ffi::gpointer,
);
list = ffi::g_slist_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[1]) as ffi::gpointer,
);
list = ffi::g_slist_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[2]) as ffi::gpointer,
);
list = ffi::g_slist_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[3]) as ffi::gpointer,
);
SList::<crate::DateTime>::from_glib_full(list)
};
assert!(!list.is_empty());
let list_items = list.iter().cloned().collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list_items = list.iter_mut().map(|d| d.clone()).collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list_items = list.into_iter().collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list = unsafe { SList::<crate::DateTime>::from_glib_full(ptr::null_mut()) };
assert!(list.is_empty());
}
#[test]
// checker-ignore-item
fn from_glib_container() {
let items = [
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
];
let mut list = unsafe {
let mut list = ffi::g_slist_append(
ptr::null_mut(),
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[0]).0 as ffi::gpointer,
);
list = ffi::g_slist_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[1]).0 as ffi::gpointer,
);
list = ffi::g_slist_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[2]).0 as ffi::gpointer,
);
list = ffi::g_slist_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[3]).0 as ffi::gpointer,
);
SList::<crate::DateTime>::from_glib_container(list)
};
assert!(!list.is_empty());
let list_items = list.iter().cloned().collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list_items = list.iter_mut().map(|d| d.clone()).collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list_items = list.into_iter().collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list = unsafe { SList::<crate::DateTime>::from_glib_full(ptr::null_mut()) };
assert!(list.is_empty());
}
#[test]
// checker-ignore-item
fn from_glib_none() {
let items = [
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
];
let mut list = unsafe {
let mut list = ffi::g_slist_append(
ptr::null_mut(),
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[0]).0 as ffi::gpointer,
);
list = ffi::g_slist_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[1]).0 as ffi::gpointer,
);
list = ffi::g_slist_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[2]).0 as ffi::gpointer,
);
list = ffi::g_slist_append(
list,
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[3]).0 as ffi::gpointer,
);
let res = SList::<crate::DateTime>::from_glib_none(list);
ffi::g_slist_free(list);
res
};
assert!(!list.is_empty());
let list_items = list.iter().cloned().collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list_items = list.iter_mut().map(|d| d.clone()).collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list_items = list.into_iter().collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
let list = unsafe { SList::<crate::DateTime>::from_glib_full(ptr::null_mut()) };
assert!(list.is_empty());
}
#[test]
// checker-ignore-item
fn safe_api() {
let items = [
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
];
let mut list = items[1..3].iter().cloned().collect::<SList<_>>();
assert_eq!(list.len(), 2);
list.push_front(items[0].clone());
assert_eq!(list.len(), 3);
list.push_back(items[3].clone());
assert_eq!(list.len(), 4);
let list_items = list.iter().cloned().collect::<Vec<_>>();
assert_eq!(&items[..], &list_items);
assert_eq!(list.front(), Some(&items[0]));
assert_eq!(list.back(), Some(&items[3]));
assert_eq!(list.pop_front().as_ref(), Some(&items[0]));
assert_eq!(list.len(), 3);
list.reverse();
let mut list_items = list.iter().cloned().collect::<Vec<_>>();
list_items.reverse();
assert_eq!(&items[1..], &list_items);
let list2 = list.clone();
let mut list_items = list2.iter().cloned().collect::<Vec<_>>();
list_items.reverse();
assert_eq!(&items[1..], &list_items);
}
}
File diff suppressed because it is too large. Load diff
+103
View File
@@ -0,0 +1,103 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use crate::translate::*;
// rustdoc-stripper-ignore-next
/// Continue calling the closure in the future iterations or drop it.
///
/// This is the return type of `idle_add` and `timeout_add` closures.
///
/// `ControlFlow::Continue` keeps the closure assigned, to be rerun when appropriate.
///
/// `ControlFlow::Break` disconnects and drops it.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ControlFlow {
Continue,
Break,
}
impl ControlFlow {
// rustdoc-stripper-ignore-next
/// Returns `true` if this is a `Continue` variant.
pub fn is_continue(&self) -> bool {
matches!(self, Self::Continue)
}
// rustdoc-stripper-ignore-next
/// Returns `true` if this is a `Break` variant.
pub fn is_break(&self) -> bool {
matches!(self, Self::Break)
}
}
impl From<std::ops::ControlFlow<()>> for ControlFlow {
fn from(c: std::ops::ControlFlow<()>) -> Self {
match c {
std::ops::ControlFlow::Break(_) => Self::Break,
std::ops::ControlFlow::Continue(_) => Self::Continue,
}
}
}
impl From<ControlFlow> for std::ops::ControlFlow<()> {
fn from(c: ControlFlow) -> Self {
match c {
ControlFlow::Break => Self::Break(()),
ControlFlow::Continue => Self::Continue(()),
}
}
}
impl From<bool> for ControlFlow {
fn from(c: bool) -> Self {
if c {
Self::Continue
} else {
Self::Break
}
}
}
impl From<ControlFlow> for bool {
fn from(c: ControlFlow) -> Self {
match c {
ControlFlow::Break => false,
ControlFlow::Continue => true,
}
}
}
#[doc(hidden)]
impl IntoGlib for ControlFlow {
type GlibType = ffi::gboolean;
#[inline]
fn into_glib(self) -> ffi::gboolean {
bool::from(self).into_glib()
}
}
#[doc(hidden)]
impl FromGlib<ffi::gboolean> for ControlFlow {
#[inline]
unsafe fn from_glib(value: ffi::gboolean) -> Self {
bool::from_glib(value).into()
}
}
impl crate::ToValue for ControlFlow {
fn to_value(&self) -> crate::Value {
bool::from(*self).to_value()
}
fn value_type(&self) -> crate::Type {
<bool as crate::StaticType>::static_type()
}
}
impl From<ControlFlow> for crate::Value {
#[inline]
fn from(v: ControlFlow) -> Self {
bool::from(v).into()
}
}
+448
View File
@@ -0,0 +1,448 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{io, os::raw::c_char, path::PathBuf, ptr};
use crate::{
translate::*, ConvertError, Error, GString, IntoGStr, IntoOptionalGStr, NormalizeMode, Slice,
};
// rustdoc-stripper-ignore-next
/// A wrapper for [`ConvertError`](crate::ConvertError) that can hold an offset into the input
/// string.
#[derive(thiserror::Error, Debug)]
pub enum CvtError {
#[error(transparent)]
Convert(#[from] Error),
#[error("{source} at offset {offset}")]
IllegalSequence {
#[source]
source: Error,
offset: usize,
},
}
impl CvtError {
#[inline]
fn new(err: Error, bytes_read: usize) -> Self {
if err.kind::<ConvertError>() == Some(ConvertError::IllegalSequence) {
Self::IllegalSequence {
source: err,
offset: bytes_read,
}
} else {
err.into()
}
}
}
#[doc(alias = "g_convert")]
pub fn convert(
str_: &[u8],
to_codeset: impl IntoGStr,
from_codeset: impl IntoGStr,
) -> Result<(Slice<u8>, usize), CvtError> {
assert!(str_.len() <= isize::MAX as usize);
let mut bytes_read = 0;
let mut bytes_written = 0;
let mut error = ptr::null_mut();
let result = to_codeset.run_with_gstr(|to_codeset| {
from_codeset.run_with_gstr(|from_codeset| unsafe {
ffi::g_convert(
str_.as_ptr(),
str_.len() as isize,
to_codeset.to_glib_none().0,
from_codeset.to_glib_none().0,
&mut bytes_read,
&mut bytes_written,
&mut error,
)
})
});
if result.is_null() {
Err(CvtError::new(unsafe { from_glib_full(error) }, bytes_read))
} else {
let slice = unsafe { Slice::from_glib_full_num(result, bytes_written as _) };
Ok((slice, bytes_read))
}
}
#[doc(alias = "g_convert_with_fallback")]
pub fn convert_with_fallback(
str_: &[u8],
to_codeset: impl IntoGStr,
from_codeset: impl IntoGStr,
fallback: Option<impl IntoGStr>,
) -> Result<(Slice<u8>, usize), CvtError> {
assert!(str_.len() <= isize::MAX as usize);
let mut bytes_read = 0;
let mut bytes_written = 0;
let mut error = ptr::null_mut();
let result = to_codeset.run_with_gstr(|to_codeset| {
from_codeset.run_with_gstr(|from_codeset| {
fallback.run_with_gstr(|fallback| unsafe {
ffi::g_convert_with_fallback(
str_.as_ptr(),
str_.len() as isize,
to_codeset.to_glib_none().0,
from_codeset.to_glib_none().0,
fallback.to_glib_none().0,
&mut bytes_read,
&mut bytes_written,
&mut error,
)
})
})
});
if result.is_null() {
Err(CvtError::new(unsafe { from_glib_full(error) }, bytes_read))
} else {
let slice = unsafe { Slice::from_glib_full_num(result, bytes_written as _) };
Ok((slice, bytes_read))
}
}
// rustdoc-stripper-ignore-next
/// A wrapper for [`std::io::Error`] that can hold an offset into an input string.
#[derive(thiserror::Error, Debug)]
pub enum IConvError {
#[error(transparent)]
Error(#[from] io::Error),
#[error("{source} at offset {offset}")]
WithOffset {
#[source]
source: io::Error,
offset: usize,
},
}
#[derive(Debug)]
#[repr(transparent)]
#[doc(alias = "GIConv")]
pub struct IConv(ffi::GIConv);
unsafe impl Send for IConv {}
impl IConv {
#[doc(alias = "g_iconv_open")]
#[allow(clippy::unnecessary_lazy_evaluations)]
pub fn new(to_codeset: impl IntoGStr, from_codeset: impl IntoGStr) -> Option<Self> {
let iconv = to_codeset.run_with_gstr(|to_codeset| {
from_codeset.run_with_gstr(|from_codeset| unsafe {
ffi::g_iconv_open(to_codeset.to_glib_none().0, from_codeset.to_glib_none().0)
})
});
(iconv as isize != -1).then(|| Self(iconv))
}
#[doc(alias = "g_convert_with_iconv")]
pub fn convert(&mut self, str_: &[u8]) -> Result<(Slice<u8>, usize), CvtError> {
assert!(str_.len() <= isize::MAX as usize);
let mut bytes_read = 0;
let mut bytes_written = 0;
let mut error = ptr::null_mut();
let result = unsafe {
ffi::g_convert_with_iconv(
str_.as_ptr(),
str_.len() as isize,
self.0,
&mut bytes_read,
&mut bytes_written,
&mut error,
)
};
if result.is_null() {
Err(CvtError::new(unsafe { from_glib_full(error) }, bytes_read))
} else {
let slice = unsafe { Slice::from_glib_full_num(result, bytes_written as _) };
Ok((slice, bytes_read))
}
}
#[doc(alias = "g_iconv")]
pub fn iconv(
&mut self,
inbuf: Option<&[u8]>,
outbuf: Option<&mut [std::mem::MaybeUninit<u8>]>,
) -> Result<(usize, usize, usize), IConvError> {
let input_len = inbuf.as_ref().map(|b| b.len()).unwrap_or_default();
let mut inbytes_left = input_len;
let mut outbytes_left = outbuf.as_ref().map(|b| b.len()).unwrap_or_default();
let mut inbuf = inbuf
.map(|b| mut_override(b.as_ptr()) as *mut c_char)
.unwrap_or_else(ptr::null_mut);
let mut outbuf = outbuf
.map(|b| b.as_mut_ptr() as *mut c_char)
.unwrap_or_else(ptr::null_mut);
let conversions = unsafe {
ffi::g_iconv(
self.0,
&mut inbuf,
&mut inbytes_left,
&mut outbuf,
&mut outbytes_left,
)
};
if conversions as isize == -1 {
let err = io::Error::last_os_error();
let code = err.raw_os_error().unwrap();
if code == libc::EILSEQ || code == libc::EINVAL {
Err(IConvError::WithOffset {
source: err,
offset: input_len - inbytes_left,
})
} else {
Err(err.into())
}
} else {
Ok((conversions, inbytes_left, outbytes_left))
}
}
}
impl Drop for IConv {
#[inline]
fn drop(&mut self) {
unsafe {
ffi::g_iconv_close(self.0);
}
}
}
#[doc(alias = "g_get_filename_charsets")]
#[doc(alias = "get_filename_charsets")]
pub fn filename_charsets() -> (bool, Vec<GString>) {
let mut filename_charsets = ptr::null_mut();
unsafe {
let is_utf8 = ffi::g_get_filename_charsets(&mut filename_charsets);
(
from_glib(is_utf8),
FromGlibPtrContainer::from_glib_none(filename_charsets),
)
}
}
#[doc(alias = "g_filename_from_utf8")]
pub fn filename_from_utf8(utf8string: impl IntoGStr) -> Result<(PathBuf, usize), CvtError> {
let mut bytes_read = 0;
let mut bytes_written = std::mem::MaybeUninit::uninit();
let mut error = ptr::null_mut();
let ret = utf8string.run_with_gstr(|utf8string| {
assert!(utf8string.len() <= isize::MAX as usize);
let len = utf8string.len() as isize;
unsafe {
ffi::g_filename_from_utf8(
utf8string.to_glib_none().0,
len,
&mut bytes_read,
bytes_written.as_mut_ptr(),
&mut error,
)
}
});
if error.is_null() {
Ok(unsafe {
(
PathBuf::from_glib_full_num(ret, bytes_written.assume_init()),
bytes_read,
)
})
} else {
Err(unsafe { CvtError::new(from_glib_full(error), bytes_read) })
}
}
#[doc(alias = "g_filename_to_utf8")]
pub fn filename_to_utf8(
opsysstring: impl AsRef<std::path::Path>,
) -> Result<(crate::GString, usize), CvtError> {
let path = opsysstring.as_ref().to_glib_none();
let mut bytes_read = 0;
let mut bytes_written = std::mem::MaybeUninit::uninit();
let mut error = ptr::null_mut();
let ret = unsafe {
ffi::g_filename_to_utf8(
path.0,
path.1.as_bytes().len() as isize,
&mut bytes_read,
bytes_written.as_mut_ptr(),
&mut error,
)
};
if error.is_null() {
Ok(unsafe {
(
GString::from_glib_full_num(ret, bytes_written.assume_init()),
bytes_read,
)
})
} else {
Err(unsafe { CvtError::new(from_glib_full(error), bytes_read) })
}
}
#[doc(alias = "g_locale_from_utf8")]
pub fn locale_from_utf8(utf8string: impl IntoGStr) -> Result<(Slice<u8>, usize), CvtError> {
let mut bytes_read = 0;
let mut bytes_written = std::mem::MaybeUninit::uninit();
let mut error = ptr::null_mut();
let ret = utf8string.run_with_gstr(|utf8string| {
assert!(utf8string.len() <= isize::MAX as usize);
unsafe {
ffi::g_locale_from_utf8(
utf8string.as_ptr(),
utf8string.len() as isize,
&mut bytes_read,
bytes_written.as_mut_ptr(),
&mut error,
)
}
});
if error.is_null() {
Ok(unsafe {
(
Slice::from_glib_full_num(ret, bytes_written.assume_init() + 1),
bytes_read,
)
})
} else {
Err(unsafe { CvtError::new(from_glib_full(error), bytes_read) })
}
}
#[doc(alias = "g_locale_to_utf8")]
pub fn locale_to_utf8(opsysstring: &[u8]) -> Result<(crate::GString, usize), CvtError> {
let len = opsysstring.len() as isize;
let mut bytes_read = 0;
let mut bytes_written = std::mem::MaybeUninit::uninit();
let mut error = ptr::null_mut();
let ret = unsafe {
ffi::g_locale_to_utf8(
opsysstring.to_glib_none().0,
len,
&mut bytes_read,
bytes_written.as_mut_ptr(),
&mut error,
)
};
if error.is_null() {
Ok(unsafe {
(
GString::from_glib_full_num(ret, bytes_written.assume_init()),
bytes_read,
)
})
} else {
Err(unsafe { CvtError::new(from_glib_full(error), bytes_read) })
}
}
#[doc(alias = "g_utf8_to_ucs4")]
#[doc(alias = "g_utf8_to_ucs4_fast")]
#[doc(alias = "utf8_to_ucs4")]
pub fn utf8_to_utf32(str: impl AsRef<str>) -> Slice<char> {
unsafe {
let mut items_written = 0;
let str_as_utf32 = ffi::g_utf8_to_ucs4_fast(
str.as_ref().as_ptr().cast::<c_char>(),
str.as_ref().len() as _,
&mut items_written,
);
// NOTE: We assume that u32 and char have the same layout and trust that glib won't give us
// invalid UTF-32 codepoints
Slice::from_glib_full_num(str_as_utf32, items_written as usize)
}
}
#[doc(alias = "g_ucs4_to_utf8")]
#[doc(alias = "ucs4_to_utf8")]
pub fn utf32_to_utf8(str: impl AsRef<[char]>) -> GString {
let mut items_read = 0;
let mut items_written = 0;
let mut error = ptr::null_mut();
unsafe {
let str_as_utf8 = ffi::g_ucs4_to_utf8(
str.as_ref().as_ptr().cast::<u32>(),
str.as_ref().len() as _,
&mut items_read,
&mut items_written,
&mut error,
);
debug_assert!(
error.is_null(),
"Rust `char` should always be convertible to UTF-8"
);
GString::from_glib_full_num(str_as_utf8, items_written as usize)
}
}
#[doc(alias = "g_utf8_casefold")]
#[doc(alias = "utf8_casefold")]
pub fn casefold(str: impl AsRef<str>) -> GString {
unsafe {
let str = ffi::g_utf8_casefold(str.as_ref().as_ptr().cast(), str.as_ref().len() as isize);
from_glib_full(str)
}
}
#[doc(alias = "g_utf8_normalize")]
#[doc(alias = "utf8_normalize")]
pub fn normalize(str: impl AsRef<str>, mode: NormalizeMode) -> GString {
unsafe {
let str = ffi::g_utf8_normalize(
str.as_ref().as_ptr().cast(),
str.as_ref().len() as isize,
mode.into_glib(),
);
from_glib_full(str)
}
}
#[cfg(test)]
mod tests {
#[test]
fn convert_ascii() {
assert!(super::convert(b"Hello", "utf-8", "ascii").is_ok());
assert!(super::convert(b"He\xaallo", "utf-8", "ascii").is_err());
assert_eq!(
super::convert_with_fallback(b"H\xc3\xa9llo", "ascii", "utf-8", crate::NONE_STR)
.unwrap()
.0
.as_slice(),
b"H\\u00e9llo"
);
assert_eq!(
super::convert_with_fallback(b"H\xc3\xa9llo", "ascii", "utf-8", Some("_"))
.unwrap()
.0
.as_slice(),
b"H_llo"
);
}
#[test]
fn iconv() {
let mut conv = super::IConv::new("utf-8", "ascii").unwrap();
assert!(conv.convert(b"Hello").is_ok());
assert!(conv.convert(b"He\xaallo").is_err());
assert!(super::IConv::new("utf-8", "badcharset123456789").is_none());
}
#[test]
fn filename_charsets() {
let _ = super::filename_charsets();
}
#[test]
fn utf8_and_utf32() {
let utf32 = ['A', 'b', '🤔'];
let utf8 = super::utf32_to_utf8(utf32);
assert_eq!(utf8, "Ab🤔");
let utf8 = "🤔 ț";
let utf32 = super::utf8_to_utf32(utf8);
assert_eq!(utf32.as_slice(), &['🤔', ' ', 'ț']);
}
}
+443
View File
@@ -0,0 +1,443 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{cmp, fmt, hash};
use crate::{translate::*, BoolError, DateDay, DateMonth, DateWeekday, DateYear};
wrapper! {
#[doc(alias = "GDate")]
pub struct Date(BoxedInline<ffi::GDate>);
match fn {
copy => |ptr| gobject_ffi::g_boxed_copy(ffi::g_date_get_type(), ptr as *const _) as *mut _,
free => |ptr| ffi::g_date_free(ptr),
type_ => || ffi::g_date_get_type(),
}
}
unsafe impl Send for Date {}
unsafe impl Sync for Date {}
impl Date {
#[doc(alias = "g_date_new_dmy")]
pub fn from_dmy(day: DateDay, month: DateMonth, year: DateYear) -> Result<Date, BoolError> {
let month = month.into_glib();
unsafe {
let check: bool = from_glib(ffi::g_date_valid_dmy(day, month, year));
if !check {
Err(bool_error!("Invalid date"))
} else {
Ok(from_glib_full(ffi::g_date_new_dmy(day, month, year)))
}
}
}
#[doc(alias = "g_date_new_julian")]
pub fn from_julian(julian_day: u32) -> Result<Date, BoolError> {
if !Self::valid_julian(julian_day) {
Err(bool_error!("Invalid date"))
} else {
unsafe { Ok(from_glib_full(ffi::g_date_new_julian(julian_day))) }
}
}
#[doc(alias = "g_date_add_days")]
pub fn add_days(&mut self, n_days: u32) -> Result<(), BoolError> {
let julian_days = self.julian();
if julian_days == 0 || n_days > u32::MAX - julian_days {
Err(bool_error!("Invalid date"))
} else {
unsafe {
ffi::g_date_add_days(self.to_glib_none_mut().0, n_days);
}
Ok(())
}
}
#[doc(alias = "g_date_add_months")]
pub fn add_months(&mut self, n_months: u32) -> Result<(), BoolError> {
// The checks for this function are just a mess in the C code, allowing intermediate
// unknown state. So for now, nothing can be done...
unsafe {
ffi::g_date_add_months(self.to_glib_none_mut().0, n_months);
}
Ok(())
}
#[doc(alias = "g_date_add_years")]
pub fn add_years(&mut self, n_years: u16) -> Result<(), BoolError> {
let year = self.year();
if n_years > u16::MAX - year {
Err(bool_error!("Invalid date"))
} else {
unsafe {
ffi::g_date_add_years(self.to_glib_none_mut().0, n_years as _);
}
Ok(())
}
}
#[doc(alias = "g_date_clamp")]
pub fn clamp(&mut self, min_date: &Date, max_date: &Date) -> Result<(), BoolError> {
if min_date >= max_date {
Err(bool_error!("`min_date` must be before `max_date`"))
} else {
unsafe {
ffi::g_date_clamp(
self.to_glib_none_mut().0,
min_date.to_glib_none().0,
max_date.to_glib_none().0,
);
}
Ok(())
}
}
#[doc(alias = "g_date_compare")]
fn compare(&self, rhs: &Date) -> i32 {
unsafe { ffi::g_date_compare(self.to_glib_none().0, rhs.to_glib_none().0) }
}
#[doc(alias = "g_date_days_between")]
pub fn days_between(&self, date2: &Date) -> i32 {
unsafe { ffi::g_date_days_between(self.to_glib_none().0, date2.to_glib_none().0) }
}
#[doc(alias = "g_date_get_day")]
#[doc(alias = "get_day")]
pub fn day(&self) -> DateDay {
unsafe { ffi::g_date_get_day(self.to_glib_none().0) }
}
#[doc(alias = "g_date_get_day_of_year")]
#[doc(alias = "get_day_of_year")]
pub fn day_of_year(&self) -> u32 {
unsafe { ffi::g_date_get_day_of_year(self.to_glib_none().0) }
}
#[doc(alias = "g_date_get_iso8601_week_of_year")]
#[doc(alias = "get_iso8601_week_of_year")]
pub fn iso8601_week_of_year(&self) -> u32 {
unsafe { ffi::g_date_get_iso8601_week_of_year(self.to_glib_none().0) }
}
#[doc(alias = "g_date_get_julian")]
#[doc(alias = "get_julian")]
pub fn julian(&self) -> u32 {
unsafe { ffi::g_date_get_julian(self.to_glib_none().0) }
}
#[doc(alias = "g_date_get_monday_week_of_year")]
#[doc(alias = "get_monday_week_of_year")]
pub fn monday_week_of_year(&self) -> u32 {
unsafe { ffi::g_date_get_monday_week_of_year(self.to_glib_none().0) }
}
#[doc(alias = "g_date_get_month")]
#[doc(alias = "get_month")]
pub fn month(&self) -> DateMonth {
unsafe { from_glib(ffi::g_date_get_month(self.to_glib_none().0)) }
}
#[doc(alias = "g_date_get_sunday_week_of_year")]
#[doc(alias = "get_sunday_week_of_year")]
pub fn sunday_week_of_year(&self) -> u32 {
unsafe { ffi::g_date_get_sunday_week_of_year(self.to_glib_none().0) }
}
#[doc(alias = "g_date_get_weekday")]
#[doc(alias = "get_weekday")]
pub fn weekday(&self) -> DateWeekday {
unsafe { from_glib(ffi::g_date_get_weekday(self.to_glib_none().0)) }
}
#[doc(alias = "g_date_get_year")]
#[doc(alias = "get_year")]
pub fn year(&self) -> DateYear {
unsafe { ffi::g_date_get_year(self.to_glib_none().0) }
}
#[doc(alias = "g_date_is_first_of_month")]
pub fn is_first_of_month(&self) -> bool {
unsafe { from_glib(ffi::g_date_is_first_of_month(self.to_glib_none().0)) }
}
#[doc(alias = "g_date_is_last_of_month")]
pub fn is_last_of_month(&self) -> bool {
unsafe { from_glib(ffi::g_date_is_last_of_month(self.to_glib_none().0)) }
}
#[doc(alias = "g_date_order")]
pub fn order(&mut self, date2: &mut Date) {
unsafe {
ffi::g_date_order(self.to_glib_none_mut().0, date2.to_glib_none_mut().0);
}
}
#[doc(alias = "g_date_set_day")]
pub fn set_day(&mut self, day: DateDay) -> Result<(), BoolError> {
if !Self::valid_dmy(day, self.month(), self.year()) {
Err(bool_error!("invalid day"))
} else {
unsafe {
ffi::g_date_set_day(self.to_glib_none_mut().0, day);
}
Ok(())
}
}
#[doc(alias = "g_date_set_dmy")]
pub fn set_dmy(
&mut self,
day: DateDay,
month: DateMonth,
y: DateYear,
) -> Result<(), BoolError> {
if !Self::valid_dmy(day, month, y) {
Err(bool_error!("invalid date"))
} else {
unsafe {
ffi::g_date_set_dmy(self.to_glib_none_mut().0, day, month.into_glib(), y);
}
Ok(())
}
}
#[doc(alias = "g_date_set_julian")]
pub fn set_julian(&mut self, julian_date: u32) -> Result<(), BoolError> {
if !Self::valid_julian(julian_date) {
Err(bool_error!("invalid date"))
} else {
unsafe {
ffi::g_date_set_julian(self.to_glib_none_mut().0, julian_date);
}
Ok(())
}
}
#[doc(alias = "g_date_set_month")]
pub fn set_month(&mut self, month: DateMonth) -> Result<(), BoolError> {
if !Self::valid_dmy(self.day(), month, self.year()) {
Err(bool_error!("invalid month"))
} else {
unsafe {
ffi::g_date_set_month(self.to_glib_none_mut().0, month.into_glib());
}
Ok(())
}
}
#[doc(alias = "g_date_set_parse")]
pub fn set_parse(&mut self, str: &str) -> Result<(), BoolError> {
let mut c = *self;
if !unsafe {
ffi::g_date_set_parse(c.to_glib_none_mut().0, str.to_glib_none().0);
ffi::g_date_valid(c.to_glib_none().0) == 0
} {
Err(bool_error!("invalid parse string"))
} else {
*self = c;
Ok(())
}
}
#[doc(alias = "g_date_set_time_t")]
pub fn set_time(&mut self, time_: u32) -> Result<(), BoolError> {
let mut c = *self;
unsafe {
ffi::g_date_set_time_t(c.to_glib_none_mut().0, time_ as _);
}
if !Self::valid_dmy(c.day(), c.month(), c.year()) {
Err(bool_error!("invalid time"))
} else {
*self = c;
Ok(())
}
}
//pub fn set_time_val(&mut self, timeval: /*Ignored*/&mut TimeVal) {
// unsafe { TODO: call ffi::g_date_set_time_val() }
//}
#[doc(alias = "g_date_set_year")]
pub fn set_year(&mut self, year: DateYear) -> Result<(), BoolError> {
if !Self::valid_dmy(self.day(), self.month(), year) {
Err(bool_error!("invalid year"))
} else {
unsafe {
ffi::g_date_set_year(self.to_glib_none_mut().0, year);
}
Ok(())
}
}
#[doc(alias = "g_date_subtract_days")]
pub fn subtract_days(&mut self, n_days: u32) -> Result<(), BoolError> {
let julian = self.julian();
if julian > n_days {
Err(bool_error!("invalid number of days"))
} else {
unsafe {
ffi::g_date_subtract_days(self.to_glib_none_mut().0, n_days);
}
Ok(())
}
}
#[doc(alias = "g_date_subtract_months")]
pub fn subtract_months(&mut self, n_months: u32) -> Result<(), BoolError> {
// The checks for this function are just a mess in the C code, allowing intermediate
// unknown state. So for now, nothing can be done...
unsafe {
ffi::g_date_subtract_months(self.to_glib_none_mut().0, n_months);
}
Ok(())
}
#[doc(alias = "g_date_subtract_years")]
pub fn subtract_years(&mut self, n_years: u16) -> Result<(), BoolError> {
if self.year() < n_years {
Err(bool_error!("invalid number of years"))
} else {
unsafe {
ffi::g_date_subtract_years(self.to_glib_none_mut().0, n_years as _);
}
Ok(())
}
}
//#[doc(alias="g_date_to_struct_tm")]
//pub fn to_struct_tm(&self, tm: /*Unimplemented*/Fundamental: Pointer) {
// unsafe { TODO: call ffi::g_date_to_struct_tm() }
//}
#[doc(alias = "g_date_valid")]
pub fn valid(&self) -> bool {
unsafe { from_glib(ffi::g_date_valid(self.to_glib_none().0)) }
}
#[doc(alias = "g_date_get_days_in_month")]
#[doc(alias = "get_days_in_month")]
pub fn days_in_month(month: DateMonth, year: DateYear) -> u8 {
unsafe { ffi::g_date_get_days_in_month(month.into_glib(), year) }
}
#[doc(alias = "g_date_get_monday_weeks_in_year")]
#[doc(alias = "get_monday_weeks_in_year")]
pub fn monday_weeks_in_year(year: DateYear) -> u8 {
unsafe { ffi::g_date_get_monday_weeks_in_year(year) }
}
#[doc(alias = "g_date_get_sunday_weeks_in_year")]
#[doc(alias = "get_sunday_weeks_in_year")]
pub fn sunday_weeks_in_year(year: DateYear) -> u8 {
unsafe { ffi::g_date_get_sunday_weeks_in_year(year) }
}
#[doc(alias = "g_date_is_leap_year")]
pub fn is_leap_year(year: DateYear) -> bool {
unsafe { from_glib(ffi::g_date_is_leap_year(year)) }
}
#[doc(alias = "g_date_strftime")]
pub fn strftime(s: &str, format: &str, date: &Date) -> usize {
let slen = s.len() as _;
unsafe {
ffi::g_date_strftime(
s.to_glib_none().0,
slen,
format.to_glib_none().0,
date.to_glib_none().0,
)
}
}
#[doc(alias = "g_date_valid_day")]
pub fn valid_day(day: DateDay) -> bool {
unsafe { from_glib(ffi::g_date_valid_day(day)) }
}
#[doc(alias = "g_date_valid_dmy")]
pub fn valid_dmy(day: DateDay, month: DateMonth, year: DateYear) -> bool {
unsafe { from_glib(ffi::g_date_valid_dmy(day, month.into_glib(), year)) }
}
#[doc(alias = "g_date_valid_julian")]
pub fn valid_julian(julian_date: u32) -> bool {
unsafe { from_glib(ffi::g_date_valid_julian(julian_date)) }
}
#[doc(alias = "g_date_valid_month")]
pub fn valid_month(month: DateMonth) -> bool {
unsafe { from_glib(ffi::g_date_valid_month(month.into_glib())) }
}
#[doc(alias = "g_date_valid_weekday")]
pub fn valid_weekday(weekday: DateWeekday) -> bool {
unsafe { from_glib(ffi::g_date_valid_weekday(weekday.into_glib())) }
}
#[doc(alias = "g_date_valid_year")]
pub fn valid_year(year: DateYear) -> bool {
unsafe { from_glib(ffi::g_date_valid_year(year)) }
}
}
impl PartialEq for Date {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.compare(other) == 0
}
}
impl Eq for Date {}
impl PartialOrd for Date {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Date {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.compare(other).cmp(&0)
}
}
impl fmt::Debug for Date {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Date")
.field("year", &self.year())
.field("month", &self.month())
.field("day", &self.day())
.finish()
}
}
impl hash::Hash for Date {
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher,
{
self.year().hash(state);
self.month().hash(state);
self.day().hash(state);
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::value::ToValue;
#[test]
fn test_value() {
let d1 = Date::from_dmy(20, crate::DateMonth::November, 2021).unwrap();
let v = d1.to_value();
let d2 = v.get::<&Date>().unwrap();
assert_eq!(&d1, d2);
}
}
+15
View File
@@ -0,0 +1,15 @@
// Take a look at the license at the top of the repository in the LICENSE file.
#[cfg(test)]
mod tests {
use crate::{prelude::*, DateTime};
#[test]
fn test_value() {
let dt1 = DateTime::now_utc().unwrap();
let v = dt1.to_value();
let dt2 = v.get::<&DateTime>().unwrap();
assert_eq!(dt1.as_ptr(), dt2.as_ptr());
}
}
+966
View File
@@ -0,0 +1,966 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{cmp, ffi::CStr, fmt, ptr};
use crate::{
translate::*,
value::{FromValue, ValueTypeChecker},
HasParamSpec, ParamSpecEnum, ParamSpecFlags, StaticType, Type, Value,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum UserDirectory {
#[doc(alias = "G_USER_DIRECTORY_DESKTOP")]
Desktop,
#[doc(alias = "G_USER_DIRECTORY_DOCUMENTS")]
Documents,
#[doc(alias = "G_USER_DIRECTORY_DOWNLOAD")]
Downloads,
#[doc(alias = "G_USER_DIRECTORY_MUSIC")]
Music,
#[doc(alias = "G_USER_DIRECTORY_PICTURES")]
Pictures,
#[doc(alias = "G_USER_DIRECTORY_PUBLIC_SHARE")]
PublicShare,
#[doc(alias = "G_USER_DIRECTORY_TEMPLATES")]
Templates,
#[doc(alias = "G_USER_DIRECTORY_VIDEOS")]
Videos,
}
#[doc(hidden)]
impl IntoGlib for UserDirectory {
type GlibType = ffi::GUserDirectory;
#[inline]
fn into_glib(self) -> ffi::GUserDirectory {
match self {
Self::Desktop => ffi::G_USER_DIRECTORY_DESKTOP,
Self::Documents => ffi::G_USER_DIRECTORY_DOCUMENTS,
Self::Downloads => ffi::G_USER_DIRECTORY_DOWNLOAD,
Self::Music => ffi::G_USER_DIRECTORY_MUSIC,
Self::Pictures => ffi::G_USER_DIRECTORY_PICTURES,
Self::PublicShare => ffi::G_USER_DIRECTORY_PUBLIC_SHARE,
Self::Templates => ffi::G_USER_DIRECTORY_TEMPLATES,
Self::Videos => ffi::G_USER_DIRECTORY_VIDEOS,
}
}
}
// rustdoc-stripper-ignore-next
/// Representation of an `enum` for dynamically, at runtime, querying the values of the enum and
/// using them.
#[doc(alias = "GEnumClass")]
#[repr(transparent)]
pub struct EnumClass(ptr::NonNull<gobject_ffi::GEnumClass>);
unsafe impl Send for EnumClass {}
unsafe impl Sync for EnumClass {}
impl fmt::Debug for EnumClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EnumClass")
.field("type", &self.type_())
.field("values", &self.values())
.finish()
}
}
impl EnumClass {
// rustdoc-stripper-ignore-next
/// Create a new `EnumClass` from a static type `T`.
///
/// Panics if `T` is not representing an enum.
pub fn new<T: StaticType + HasParamSpec<ParamSpec = ParamSpecEnum>>() -> Self {
Self::with_type(T::static_type()).expect("invalid enum class")
}
// rustdoc-stripper-ignore-next
/// Create a new `EnumClass` from a `Type`.
///
/// Returns `None` if `type_` is not representing an enum.
pub fn with_type(type_: Type) -> Option<Self> {
unsafe {
let is_enum: bool = from_glib(gobject_ffi::g_type_is_a(
type_.into_glib(),
gobject_ffi::G_TYPE_ENUM,
));
if !is_enum {
return None;
}
Some(EnumClass(
ptr::NonNull::new(gobject_ffi::g_type_class_ref(type_.into_glib()) as *mut _)
.unwrap(),
))
}
}
// rustdoc-stripper-ignore-next
/// `Type` of the enum.
pub fn type_(&self) -> Type {
unsafe { from_glib(self.0.as_ref().g_type_class.g_type) }
}
// rustdoc-stripper-ignore-next
/// Gets `EnumValue` by integer `value`, if existing.
///
/// Returns `None` if the enum does not contain any value
/// with `value`.
#[doc(alias = "g_enum_get_value")]
#[doc(alias = "get_value")]
pub fn value(&self, value: i32) -> Option<&EnumValue> {
unsafe {
let v = gobject_ffi::g_enum_get_value(self.0.as_ptr(), value);
if v.is_null() {
None
} else {
Some(&*(v as *const EnumValue))
}
}
}
// rustdoc-stripper-ignore-next
/// Gets `EnumValue` by string name `name`, if existing.
///
/// Returns `None` if the enum does not contain any value
/// with name `name`.
#[doc(alias = "g_enum_get_value_by_name")]
#[doc(alias = "get_value_by_name")]
pub fn value_by_name(&self, name: &str) -> Option<&EnumValue> {
unsafe {
let v = gobject_ffi::g_enum_get_value_by_name(self.0.as_ptr(), name.to_glib_none().0);
if v.is_null() {
None
} else {
Some(&*(v as *const EnumValue))
}
}
}
// rustdoc-stripper-ignore-next
/// Gets `EnumValue` by string nick `nick`, if existing.
///
/// Returns `None` if the enum does not contain any value
/// with nick `nick`.
#[doc(alias = "g_enum_get_value_by_nick")]
#[doc(alias = "get_value_by_nick")]
pub fn value_by_nick(&self, nick: &str) -> Option<&EnumValue> {
unsafe {
let v = gobject_ffi::g_enum_get_value_by_nick(self.0.as_ptr(), nick.to_glib_none().0);
if v.is_null() {
None
} else {
Some(&*(v as *const EnumValue))
}
}
}
// rustdoc-stripper-ignore-next
/// Gets all `EnumValue` of this `EnumClass`.
#[doc(alias = "get_values")]
pub fn values(&self) -> &[EnumValue] {
unsafe {
if self.0.as_ref().n_values == 0 {
return &[];
}
std::slice::from_raw_parts(
self.0.as_ref().values as *const EnumValue,
self.0.as_ref().n_values as usize,
)
}
}
// rustdoc-stripper-ignore-next
/// Converts integer `value` to a `Value`, if part of the enum.
pub fn to_value(&self, value: i32) -> Option<Value> {
self.value(value).map(|v| v.to_value(self))
}
// rustdoc-stripper-ignore-next
/// Converts string name `name` to a `Value`, if part of the enum.
pub fn to_value_by_name(&self, name: &str) -> Option<Value> {
self.value_by_name(name).map(|v| v.to_value(self))
}
// rustdoc-stripper-ignore-next
/// Converts string nick `nick` to a `Value`, if part of the enum.
pub fn to_value_by_nick(&self, nick: &str) -> Option<Value> {
self.value_by_nick(nick).map(|v| v.to_value(self))
}
}
impl Drop for EnumClass {
#[inline]
fn drop(&mut self) {
unsafe {
gobject_ffi::g_type_class_unref(self.0.as_ptr() as *mut _);
}
}
}
impl Clone for EnumClass {
#[inline]
fn clone(&self) -> Self {
unsafe {
Self(ptr::NonNull::new(gobject_ffi::g_type_class_ref(self.type_().into_glib()) as *mut _).unwrap())
}
}
}
// rustdoc-stripper-ignore-next
/// Representation of a single enum value of an `EnumClass`.
#[doc(alias = "GEnumValue")]
#[repr(transparent)]
pub struct EnumValue(gobject_ffi::GEnumValue);
unsafe impl Send for EnumValue {}
unsafe impl Sync for EnumValue {}
impl fmt::Debug for EnumValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EnumValue")
.field("value", &self.value())
.field("name", &self.name())
.field("nick", &self.nick())
.finish()
}
}
impl EnumValue {
// rustdoc-stripper-ignore-next
/// Get integer value corresponding to the value.
#[doc(alias = "get_value")]
pub fn value(&self) -> i32 {
self.0.value
}
// rustdoc-stripper-ignore-next
/// Get name corresponding to the value.
#[doc(alias = "get_name")]
pub fn name(&self) -> &str {
unsafe { CStr::from_ptr(self.0.value_name).to_str().unwrap() }
}
// rustdoc-stripper-ignore-next
/// Get nick corresponding to the value.
#[doc(alias = "get_nick")]
pub fn nick(&self) -> &str {
unsafe { CStr::from_ptr(self.0.value_nick).to_str().unwrap() }
}
// rustdoc-stripper-ignore-next
/// Convert enum value to a `Value`.
pub fn to_value(&self, enum_: &EnumClass) -> Value {
unsafe {
let mut v = Value::from_type_unchecked(enum_.type_());
gobject_ffi::g_value_set_enum(v.to_glib_none_mut().0, self.0.value);
v
}
}
// rustdoc-stripper-ignore-next
/// Convert enum value from a `Value`.
pub fn from_value(value: &Value) -> Option<(EnumClass, &EnumValue)> {
unsafe {
let enum_class = EnumClass::with_type(value.type_())?;
let v = enum_class.value(gobject_ffi::g_value_get_enum(value.to_glib_none().0))?;
let v = &*(v as *const EnumValue);
Some((enum_class, v))
}
}
}
impl PartialEq for EnumValue {
fn eq(&self, other: &Self) -> bool {
self.value().eq(&other.value())
}
}
impl Eq for EnumValue {}
impl PartialOrd for EnumValue {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for EnumValue {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.value().cmp(&other.value())
}
}
unsafe impl<'a, 'b> FromValue<'a> for &'b EnumValue {
type Checker = EnumTypeChecker;
unsafe fn from_value(value: &'a Value) -> Self {
let (_, v) = EnumValue::from_value(value).unwrap();
// SAFETY: The enum class and its values live forever
std::mem::transmute(v)
}
}
pub struct EnumTypeChecker();
unsafe impl ValueTypeChecker for EnumTypeChecker {
type Error = InvalidEnumError;
fn check(value: &Value) -> Result<(), Self::Error> {
let t = value.type_();
if t.is_a(Type::ENUM) {
Ok(())
} else {
Err(InvalidEnumError)
}
}
}
// rustdoc-stripper-ignore-next
/// An error returned from the [`get`](struct.Value.html#method.get) function
/// on a [`Value`](struct.Value.html) for enum types.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct InvalidEnumError;
impl fmt::Display for InvalidEnumError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Value is not an enum")
}
}
impl std::error::Error for InvalidEnumError {}
// rustdoc-stripper-ignore-next
/// Representation of a `flags` for dynamically, at runtime, querying the values of the enum and
/// using them
#[doc(alias = "GFlagsClass")]
#[repr(transparent)]
pub struct FlagsClass(ptr::NonNull<gobject_ffi::GFlagsClass>);
unsafe impl Send for FlagsClass {}
unsafe impl Sync for FlagsClass {}
impl fmt::Debug for FlagsClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FlagsClass")
.field("type", &self.type_())
.field("values", &self.values())
.finish()
}
}
impl FlagsClass {
// rustdoc-stripper-ignore-next
/// Create a new `FlagsClass` from a static type `T`.
///
/// Panics if `T` is not representing an flags type.
pub fn new<T: StaticType + HasParamSpec<ParamSpec = ParamSpecFlags>>() -> Self {
Self::with_type(T::static_type()).expect("invalid flags class")
}
// rustdoc-stripper-ignore-next
/// Create a new `FlagsClass` from a `Type`
///
/// Returns `None` if `type_` is not representing a flags type.
pub fn with_type(type_: Type) -> Option<Self> {
unsafe {
let is_flags: bool = from_glib(gobject_ffi::g_type_is_a(
type_.into_glib(),
gobject_ffi::G_TYPE_FLAGS,
));
if !is_flags {
return None;
}
Some(FlagsClass(
ptr::NonNull::new(gobject_ffi::g_type_class_ref(type_.into_glib()) as *mut _)
.unwrap(),
))
}
}
// rustdoc-stripper-ignore-next
/// `Type` of the flags.
pub fn type_(&self) -> Type {
unsafe { from_glib(self.0.as_ref().g_type_class.g_type) }
}
// rustdoc-stripper-ignore-next
/// Gets `FlagsValue` by integer `value`, if existing.
///
/// Returns `None` if the flags do not contain any value
/// with `value`.
#[doc(alias = "g_flags_get_first_value")]
#[doc(alias = "get_value")]
pub fn value(&self, value: u32) -> Option<&FlagsValue> {
unsafe {
let v = gobject_ffi::g_flags_get_first_value(self.0.as_ptr(), value);
if v.is_null() {
None
} else {
Some(&*(v as *const FlagsValue))
}
}
}
// rustdoc-stripper-ignore-next
/// Gets `FlagsValue` by string name `name`, if existing.
///
/// Returns `None` if the flags do not contain any value
/// with name `name`.
#[doc(alias = "g_flags_get_value_by_name")]
#[doc(alias = "get_value_by_name")]
pub fn value_by_name(&self, name: &str) -> Option<&FlagsValue> {
unsafe {
let v = gobject_ffi::g_flags_get_value_by_name(self.0.as_ptr(), name.to_glib_none().0);
if v.is_null() {
None
} else {
Some(&*(v as *const FlagsValue))
}
}
}
// rustdoc-stripper-ignore-next
/// Gets `FlagsValue` by string nick `nick`, if existing.
///
/// Returns `None` if the flags do not contain any value
/// with nick `nick`.
#[doc(alias = "g_flags_get_value_by_nick")]
#[doc(alias = "get_value_by_nick")]
pub fn value_by_nick(&self, nick: &str) -> Option<&FlagsValue> {
unsafe {
let v = gobject_ffi::g_flags_get_value_by_nick(self.0.as_ptr(), nick.to_glib_none().0);
if v.is_null() {
None
} else {
Some(&*(v as *const FlagsValue))
}
}
}
// rustdoc-stripper-ignore-next
/// Gets all `FlagsValue` of this `FlagsClass`.
#[doc(alias = "get_values")]
pub fn values(&self) -> &[FlagsValue] {
unsafe {
if self.0.as_ref().n_values == 0 {
return &[];
}
std::slice::from_raw_parts(
self.0.as_ref().values as *const FlagsValue,
self.0.as_ref().n_values as usize,
)
}
}
// rustdoc-stripper-ignore-next
/// Converts integer `value` to a `Value`, if part of the flags.
pub fn to_value(&self, value: u32) -> Option<Value> {
self.value(value).map(|v| v.to_value(self))
}
// rustdoc-stripper-ignore-next
/// Converts string name `name` to a `Value`, if part of the flags.
pub fn to_value_by_name(&self, name: &str) -> Option<Value> {
self.value_by_name(name).map(|v| v.to_value(self))
}
// rustdoc-stripper-ignore-next
/// Converts string nick `nick` to a `Value`, if part of the flags.
pub fn to_value_by_nick(&self, nick: &str) -> Option<Value> {
self.value_by_nick(nick).map(|v| v.to_value(self))
}
// rustdoc-stripper-ignore-next
/// Checks if the flags corresponding to integer `f` is set in `value`.
pub fn is_set(&self, value: &Value, f: u32) -> bool {
unsafe {
if self.type_() != value.type_() {
return false;
}
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
flags & f != 0
}
}
// rustdoc-stripper-ignore-next
/// Checks if the flags corresponding to string name `name` is set in `value`.
pub fn is_set_by_name(&self, value: &Value, name: &str) -> bool {
unsafe {
if self.type_() != value.type_() {
return false;
}
if let Some(f) = self.value_by_name(name) {
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
flags & f.value() != 0
} else {
false
}
}
}
// rustdoc-stripper-ignore-next
/// Checks if the flags corresponding to string nick `nick` is set in `value`.
pub fn is_set_by_nick(&self, value: &Value, nick: &str) -> bool {
unsafe {
if self.type_() != value.type_() {
return false;
}
if let Some(f) = self.value_by_nick(nick) {
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
flags & f.value() != 0
} else {
false
}
}
}
// rustdoc-stripper-ignore-next
/// Set flags value corresponding to integer `f` in `value`, if part of that flags. If the
/// flag is already set, it will succeed without doing any changes.
///
/// Returns `Ok(value)` with the flag set if successful, or `Err(value)` with the original
/// value otherwise.
#[doc(alias = "g_value_set_flags")]
pub fn set(&self, mut value: Value, f: u32) -> Result<Value, Value> {
unsafe {
if self.type_() != value.type_() {
return Err(value);
}
if let Some(f) = self.value(f) {
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags | f.value());
Ok(value)
} else {
Err(value)
}
}
}
// rustdoc-stripper-ignore-next
/// Set flags value corresponding to string name `name` in `value`, if part of that flags.
/// If the flag is already set, it will succeed without doing any changes.
///
/// Returns `Ok(value)` with the flag set if successful, or `Err(value)` with the original
/// value otherwise.
pub fn set_by_name(&self, mut value: Value, name: &str) -> Result<Value, Value> {
unsafe {
if self.type_() != value.type_() {
return Err(value);
}
if let Some(f) = self.value_by_name(name) {
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags | f.value());
Ok(value)
} else {
Err(value)
}
}
}
// rustdoc-stripper-ignore-next
/// Set flags value corresponding to string nick `nick` in `value`, if part of that flags.
/// If the flag is already set, it will succeed without doing any changes.
///
/// Returns `Ok(value)` with the flag set if successful, or `Err(value)` with the original
/// value otherwise.
pub fn set_by_nick(&self, mut value: Value, nick: &str) -> Result<Value, Value> {
unsafe {
if self.type_() != value.type_() {
return Err(value);
}
if let Some(f) = self.value_by_nick(nick) {
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags | f.value());
Ok(value)
} else {
Err(value)
}
}
}
// rustdoc-stripper-ignore-next
/// Unset flags value corresponding to integer `f` in `value`, if part of that flags.
/// If the flag is already unset, it will succeed without doing any changes.
///
/// Returns `Ok(value)` with the flag unset if successful, or `Err(value)` with the original
/// value otherwise.
pub fn unset(&self, mut value: Value, f: u32) -> Result<Value, Value> {
unsafe {
if self.type_() != value.type_() {
return Err(value);
}
if let Some(f) = self.value(f) {
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags & !f.value());
Ok(value)
} else {
Err(value)
}
}
}
// rustdoc-stripper-ignore-next
/// Unset flags value corresponding to string name `name` in `value`, if part of that flags.
/// If the flag is already unset, it will succeed without doing any changes.
///
/// Returns `Ok(value)` with the flag unset if successful, or `Err(value)` with the original
/// value otherwise.
pub fn unset_by_name(&self, mut value: Value, name: &str) -> Result<Value, Value> {
unsafe {
if self.type_() != value.type_() {
return Err(value);
}
if let Some(f) = self.value_by_name(name) {
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags & !f.value());
Ok(value)
} else {
Err(value)
}
}
}
// rustdoc-stripper-ignore-next
/// Unset flags value corresponding to string nick `nick` in `value`, if part of that flags.
/// If the flag is already unset, it will succeed without doing any changes.
///
/// Returns `Ok(value)` with the flag unset if successful, or `Err(value)` with the original
/// value otherwise.
pub fn unset_by_nick(&self, mut value: Value, nick: &str) -> Result<Value, Value> {
unsafe {
if self.type_() != value.type_() {
return Err(value);
}
if let Some(f) = self.value_by_nick(nick) {
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags & !f.value());
Ok(value)
} else {
Err(value)
}
}
}
// rustdoc-stripper-ignore-next
/// Converts an integer `value` to a string of nicks separated by `|`.
pub fn to_nick_string(&self, mut value: u32) -> String {
let mut s = String::new();
for val in self.values() {
let v = val.value();
if v != 0 && (value & v) == v {
value &= !v;
if !s.is_empty() {
s.push('|');
}
s.push_str(val.nick());
}
}
s
}
// rustdoc-stripper-ignore-next
/// Converts a string of nicks `s` separated by `|` to an integer value.
pub fn from_nick_string(&self, s: &str) -> Result<u32, ParseFlagsError> {
s.split('|').try_fold(0u32, |acc, flag| {
self.value_by_nick(flag.trim())
.map(|v| acc + v.value())
.ok_or_else(|| ParseFlagsError(flag.to_owned()))
})
}
// rustdoc-stripper-ignore-next
/// Returns a new `FlagsBuilder` for conveniently setting/unsetting flags
/// and building a `Value`.
pub fn builder(&self) -> FlagsBuilder {
FlagsBuilder::new(self)
}
// rustdoc-stripper-ignore-next
/// Returns a new `FlagsBuilder` for conveniently setting/unsetting flags
/// and building a `Value`. The `Value` is initialized with `value`.
pub fn builder_with_value(&self, value: Value) -> Option<FlagsBuilder> {
if self.type_() != value.type_() {
return None;
}
Some(FlagsBuilder::with_value(self, value))
}
}
impl Drop for FlagsClass {
#[inline]
fn drop(&mut self) {
unsafe {
gobject_ffi::g_type_class_unref(self.0.as_ptr() as *mut _);
}
}
}
impl Clone for FlagsClass {
#[inline]
fn clone(&self) -> Self {
unsafe {
Self(ptr::NonNull::new(gobject_ffi::g_type_class_ref(self.type_().into_glib()) as *mut _).unwrap())
}
}
}
#[derive(Debug)]
pub struct ParseFlagsError(String);
impl std::error::Error for ParseFlagsError {}
impl fmt::Display for ParseFlagsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Unknown flag: '{}'", self.0)
}
}
impl ParseFlagsError {
pub fn flag(&self) -> &str {
&self.0
}
}
// rustdoc-stripper-ignore-next
/// Representation of a single flags value of a `FlagsClass`.
#[doc(alias = "GFlagsValue")]
#[repr(transparent)]
pub struct FlagsValue(gobject_ffi::GFlagsValue);
unsafe impl Send for FlagsValue {}
unsafe impl Sync for FlagsValue {}
impl fmt::Debug for FlagsValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FlagsValue")
.field("value", &self.value())
.field("name", &self.name())
.field("nick", &self.nick())
.finish()
}
}
impl FlagsValue {
// rustdoc-stripper-ignore-next
/// Get integer value corresponding to the value.
#[doc(alias = "get_value")]
pub fn value(&self) -> u32 {
self.0.value
}
// rustdoc-stripper-ignore-next
/// Get name corresponding to the value.
#[doc(alias = "get_name")]
pub fn name(&self) -> &str {
unsafe { CStr::from_ptr(self.0.value_name).to_str().unwrap() }
}
// rustdoc-stripper-ignore-next
/// Get nick corresponding to the value.
#[doc(alias = "get_nick")]
pub fn nick(&self) -> &str {
unsafe { CStr::from_ptr(self.0.value_nick).to_str().unwrap() }
}
// rustdoc-stripper-ignore-next
/// Convert flags value to a `Value`.
pub fn to_value(&self, flags: &FlagsClass) -> Value {
unsafe {
let mut v = Value::from_type_unchecked(flags.type_());
gobject_ffi::g_value_set_flags(v.to_glib_none_mut().0, self.0.value);
v
}
}
// rustdoc-stripper-ignore-next
/// Convert flags values from a `Value`. This returns all flags that are set.
pub fn from_value(value: &Value) -> Option<(FlagsClass, Vec<&FlagsValue>)> {
unsafe {
let flags_class = FlagsClass::with_type(value.type_())?;
let mut res = Vec::new();
let f = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
for v in flags_class.values() {
if v.value() & f != 0 {
res.push(&*(v as *const FlagsValue));
}
}
Some((flags_class, res))
}
}
}
impl PartialEq for FlagsValue {
fn eq(&self, other: &Self) -> bool {
self.value().eq(&other.value())
}
}
impl Eq for FlagsValue {}
// rustdoc-stripper-ignore-next
/// Builder for conveniently setting/unsetting flags and returning a `Value`.
///
/// Example for getting a flags property, unsetting some flags and setting the updated flags on the
/// object again:
///
/// ```ignore
/// let flags = obj.property("flags").unwrap();
/// let flags_class = FlagsClass::new(flags.type_()).unwrap();
/// let flags = flags_class.builder_with_value(flags).unwrap()
/// .unset_by_nick("some-flag")
/// .unset_by_nick("some-other-flag")
/// .build()
/// .unwrap();
/// obj.set_property("flags", &flags).unwrap();
/// ```
///
/// If setting/unsetting any value fails, `build()` returns `None`.
#[must_use = "The builder must be built to be used"]
pub struct FlagsBuilder<'a>(&'a FlagsClass, Option<Value>);
impl<'a> FlagsBuilder<'a> {
fn new(flags_class: &FlagsClass) -> FlagsBuilder {
let value = unsafe { Value::from_type_unchecked(flags_class.type_()) };
FlagsBuilder(flags_class, Some(value))
}
fn with_value(flags_class: &FlagsClass, value: Value) -> FlagsBuilder {
FlagsBuilder(flags_class, Some(value))
}
// rustdoc-stripper-ignore-next
/// Set flags corresponding to integer value `f`.
pub fn set(mut self, f: u32) -> Self {
if let Some(value) = self.1.take() {
self.1 = self.0.set(value, f).ok();
}
self
}
// rustdoc-stripper-ignore-next
/// Set flags corresponding to string name `name`.
pub fn set_by_name(mut self, name: &str) -> Self {
if let Some(value) = self.1.take() {
self.1 = self.0.set_by_name(value, name).ok();
}
self
}
// rustdoc-stripper-ignore-next
/// Set flags corresponding to string nick `nick`.
pub fn set_by_nick(mut self, nick: &str) -> Self {
if let Some(value) = self.1.take() {
self.1 = self.0.set_by_nick(value, nick).ok();
}
self
}
// rustdoc-stripper-ignore-next
/// Unsets flags corresponding to integer value `f`.
pub fn unset(mut self, f: u32) -> Self {
if let Some(value) = self.1.take() {
self.1 = self.0.unset(value, f).ok();
}
self
}
// rustdoc-stripper-ignore-next
/// Unset flags corresponding to string name `name`.
pub fn unset_by_name(mut self, name: &str) -> Self {
if let Some(value) = self.1.take() {
self.1 = self.0.unset_by_name(value, name).ok();
}
self
}
// rustdoc-stripper-ignore-next
/// Unset flags corresponding to string nick `nick`.
pub fn unset_by_nick(mut self, nick: &str) -> Self {
if let Some(value) = self.1.take() {
self.1 = self.0.unset_by_nick(value, nick).ok();
}
self
}
// rustdoc-stripper-ignore-next
/// Converts to the final `Value`, unless any previous setting/unsetting of flags failed.
#[must_use = "Value returned from the builder should probably be used"]
pub fn build(self) -> Option<Value> {
self.1
}
}
unsafe impl<'a, 'b> FromValue<'a> for Vec<&'b FlagsValue> {
type Checker = FlagsTypeChecker;
unsafe fn from_value(value: &'a Value) -> Self {
let (_, v) = FlagsValue::from_value(value).unwrap();
// SAFETY: The enum class and its values live forever
std::mem::transmute(v)
}
}
pub struct FlagsTypeChecker();
unsafe impl ValueTypeChecker for FlagsTypeChecker {
type Error = InvalidFlagsError;
fn check(value: &Value) -> Result<(), Self::Error> {
let t = value.type_();
if t.is_a(Type::FLAGS) {
Ok(())
} else {
Err(InvalidFlagsError)
}
}
}
// rustdoc-stripper-ignore-next
/// An error returned from the [`get`](struct.Value.html#method.get) function
/// on a [`Value`](struct.Value.html) for flags types.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct InvalidFlagsError;
impl fmt::Display for InvalidFlagsError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Value is not a flags")
}
}
impl std::error::Error for InvalidFlagsError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_flags() {
let flags = FlagsClass::new::<crate::BindingFlags>();
let values = flags.values();
let def1 = values
.iter()
.find(|v| v.name() == "G_BINDING_DEFAULT")
.unwrap();
let def2 = flags.value_by_name("G_BINDING_DEFAULT").unwrap();
assert!(ptr::eq(def1, def2));
let value = flags.to_value(0).unwrap();
let values = value.get::<Vec<&FlagsValue>>().unwrap();
assert_eq!(values.len(), 0);
assert_eq!(def1.value(), crate::BindingFlags::DEFAULT.bits());
}
}
+325
View File
@@ -0,0 +1,325 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! `Error` binding and helper trait.
use std::{borrow::Cow, convert::Infallible, error, ffi::CStr, fmt, str};
use crate::{translate::*, Quark};
wrapper! {
// rustdoc-stripper-ignore-next
/// A generic error capable of representing various error domains (types).
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
#[doc(alias = "GError")]
pub struct Error(Boxed<ffi::GError>);
match fn {
copy => |ptr| ffi::g_error_copy(ptr),
free => |ptr| ffi::g_error_free(ptr),
type_ => || ffi::g_error_get_type(),
}
}
unsafe impl Send for Error {}
unsafe impl Sync for Error {}
impl Error {
// rustdoc-stripper-ignore-next
/// Creates an error with supplied error enum variant and message.
#[doc(alias = "g_error_new_literal")]
#[doc(alias = "g_error_new")]
pub fn new<T: ErrorDomain>(error: T, message: &str) -> Error {
unsafe {
from_glib_full(ffi::g_error_new_literal(
T::domain().into_glib(),
error.code(),
message.to_glib_none().0,
))
}
}
// rustdoc-stripper-ignore-next
/// Checks if the error domain matches `T`.
pub fn is<T: ErrorDomain>(&self) -> bool {
self.inner.domain == T::domain().into_glib()
}
// rustdoc-stripper-ignore-next
/// Returns the error domain quark
pub fn domain(&self) -> Quark {
unsafe { from_glib(self.inner.domain) }
}
// rustdoc-stripper-ignore-next
/// Checks if the error matches the specified domain and error code.
#[doc(alias = "g_error_matches")]
pub fn matches<T: ErrorDomain>(&self, err: T) -> bool {
self.is::<T>() && self.inner.code == err.code()
}
// rustdoc-stripper-ignore-next
/// Tries to convert to a specific error enum.
///
/// Returns `Some` if the error belongs to the enum's error domain and
/// `None` otherwise.
///
/// # Examples
///
/// ```ignore
/// if let Some(file_error) = error.kind::<FileError>() {
/// match file_error {
/// FileError::Exist => ...
/// FileError::Isdir => ...
/// ...
/// }
/// }
/// ```
pub fn kind<T: ErrorDomain>(&self) -> Option<T> {
if self.is::<T>() {
T::from(self.inner.code)
} else {
None
}
}
// rustdoc-stripper-ignore-next
/// Returns the error message
///
/// Most of the time you can simply print the error since it implements the `Display`
/// trait, but you can use this method if you need to have the message as a `&str`.
pub fn message(&self) -> &str {
unsafe {
let bytes = CStr::from_ptr(self.inner.message).to_bytes();
str::from_utf8(bytes)
.unwrap_or_else(|err| str::from_utf8(&bytes[..err.valid_up_to()]).unwrap())
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.message())
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Error")
.field("domain", unsafe {
&crate::Quark::from_glib(self.inner.domain)
})
.field("code", &self.inner.code)
.field("message", &self.message())
.finish()
}
}
impl error::Error for Error {}
impl From<Infallible> for Error {
fn from(e: Infallible) -> Self {
match e {}
}
}
// rustdoc-stripper-ignore-next
/// `GLib` error domain.
///
/// This trait is implemented by error enums that represent error domains (types).
pub trait ErrorDomain: Copy {
// rustdoc-stripper-ignore-next
/// Returns the quark identifying the error domain.
///
/// As returned from `g_some_error_quark`.
fn domain() -> Quark;
// rustdoc-stripper-ignore-next
/// Gets the integer representation of the variant.
fn code(self) -> i32;
// rustdoc-stripper-ignore-next
/// Tries to convert an integer code to an enum variant.
///
/// By convention, the `Failed` variant, if present, is a catch-all,
/// i.e. any unrecognized codes map to it.
fn from(code: i32) -> Option<Self>
where
Self: Sized;
}
// rustdoc-stripper-ignore-next
/// Generic error used for functions that fail without any further information
#[macro_export]
macro_rules! bool_error(
// Plain strings
($msg:expr) => {{
$crate::BoolError::new(
$msg,
file!(),
$crate::function_name!(),
line!(),
)
}};
// Format strings
($($msg:tt)*) => {{
$crate::BoolError::new(
format!($($msg)*),
file!(),
$crate::function_name!(),
line!(),
)
}};
);
#[macro_export]
macro_rules! result_from_gboolean(
// Plain strings
($ffi_bool:expr, $msg:expr) => {{
$crate::BoolError::from_glib(
$ffi_bool,
$msg,
file!(),
$crate::function_name!(),
line!(),
)
}};
// Format strings
($ffi_bool:expr, $($msg:tt)*) => {{
$crate::BoolError::from_glib(
$ffi_bool,
format!($($msg)*),
file!(),
$crate::function_name!(),
line!(),
)
}};
);
#[derive(Debug, Clone)]
pub struct BoolError {
pub message: Cow<'static, str>,
#[doc(hidden)]
pub filename: &'static str,
#[doc(hidden)]
pub function: &'static str,
#[doc(hidden)]
pub line: u32,
}
impl BoolError {
pub fn new(
message: impl Into<Cow<'static, str>>,
filename: &'static str,
function: &'static str,
line: u32,
) -> Self {
Self {
message: message.into(),
filename,
function,
line,
}
}
pub fn from_glib(
b: ffi::gboolean,
message: impl Into<Cow<'static, str>>,
filename: &'static str,
function: &'static str,
line: u32,
) -> Result<(), Self> {
match b {
ffi::GFALSE => Err(BoolError::new(message, filename, function, line)),
_ => Ok(()),
}
}
}
impl fmt::Display for BoolError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.message)
}
}
impl error::Error for BoolError {}
#[cfg(test)]
mod tests {
use std::ffi::CString;
use super::*;
use crate::prelude::*;
#[test]
fn test_error_matches() {
let e = Error::new(crate::FileError::Failed, "Failed");
assert!(e.matches(crate::FileError::Failed));
assert!(!e.matches(crate::FileError::Again));
assert!(!e.matches(crate::KeyFileError::NotFound));
}
#[test]
fn test_error_kind() {
let e = Error::new(crate::FileError::Failed, "Failed");
assert_eq!(e.kind::<crate::FileError>(), Some(crate::FileError::Failed));
assert_eq!(e.kind::<crate::KeyFileError>(), None);
}
#[test]
fn test_into_raw() {
unsafe {
let e: *mut ffi::GError =
Error::new(crate::FileError::Failed, "Failed").into_glib_ptr();
assert_eq!((*e).domain, ffi::g_file_error_quark());
assert_eq!((*e).code, ffi::G_FILE_ERROR_FAILED);
assert_eq!(
CStr::from_ptr((*e).message),
CString::new("Failed").unwrap().as_c_str()
);
ffi::g_error_free(e);
}
}
#[test]
fn test_bool_error() {
let from_static_msg = bool_error!("Static message");
assert_eq!(from_static_msg.to_string(), "Static message");
let from_dynamic_msg = bool_error!("{} message", "Dynamic");
assert_eq!(from_dynamic_msg.to_string(), "Dynamic message");
let false_static_res = result_from_gboolean!(ffi::GFALSE, "Static message");
assert!(false_static_res.is_err());
let static_err = false_static_res.err().unwrap();
assert_eq!(static_err.to_string(), "Static message");
let true_static_res = result_from_gboolean!(ffi::GTRUE, "Static message");
assert!(true_static_res.is_ok());
let false_dynamic_res = result_from_gboolean!(ffi::GFALSE, "{} message", "Dynamic");
assert!(false_dynamic_res.is_err());
let dynamic_err = false_dynamic_res.err().unwrap();
assert_eq!(dynamic_err.to_string(), "Dynamic message");
let true_dynamic_res = result_from_gboolean!(ffi::GTRUE, "{} message", "Dynamic");
assert!(true_dynamic_res.is_ok());
}
#[test]
fn test_value() {
let e1 = Error::new(crate::FileError::Failed, "Failed");
// This creates a copy ...
let v = e1.to_value();
// ... so we have to get the raw pointer from inside the value to check for equality.
let ptr =
unsafe { gobject_ffi::g_value_get_boxed(v.to_glib_none().0) as *const ffi::GError };
let e2 = v.get::<&Error>().unwrap();
assert_eq!(ptr, e2.to_glib_none().0);
}
}
+33
View File
@@ -0,0 +1,33 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::process::Termination;
#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)]
pub struct ExitCode(i32);
impl ExitCode {
pub const SUCCESS: Self = Self(0);
pub const FAILURE: Self = Self(1);
pub fn value(&self) -> i32 {
self.0
}
}
impl From<i32> for ExitCode {
fn from(value: i32) -> Self {
Self(value)
}
}
impl From<ExitCode> for i32 {
fn from(value: ExitCode) -> Self {
value.0
}
}
impl Termination for ExitCode {
fn report(self) -> std::process::ExitCode {
std::process::ExitCode::from(self.0 as u8)
}
}
@@ -0,0 +1,103 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{error, fmt};
use futures_util::{
future::{self, Either, Future},
pin_mut,
};
// rustdoc-stripper-ignore-next
/// The error returned when a future times out.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct FutureWithTimeoutError;
impl fmt::Display for FutureWithTimeoutError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("The future timed out")
}
}
impl error::Error for FutureWithTimeoutError {}
// rustdoc-stripper-ignore-next
/// Add a timeout to a `Future`.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub async fn future_with_timeout_with_priority<T>(
priority: crate::Priority,
timeout: std::time::Duration,
fut: impl Future<Output = T>,
) -> Result<T, FutureWithTimeoutError> {
let timeout = crate::timeout_future_with_priority(priority, timeout);
pin_mut!(fut);
match future::select(fut, timeout).await {
Either::Left((x, _)) => Ok(x),
_ => Err(FutureWithTimeoutError),
}
}
// rustdoc-stripper-ignore-next
/// Add a timeout to a `Future`.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub async fn future_with_timeout<T>(
timeout: std::time::Duration,
fut: impl Future<Output = T>,
) -> Result<T, FutureWithTimeoutError> {
future_with_timeout_with_priority(crate::Priority::default(), timeout, fut).await
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use futures_util::FutureExt;
use super::*;
use crate::{MainContext, MainLoop};
#[test]
fn test_future_with_timeout() {
let c = MainContext::new();
let fut = future::pending::<()>();
let result = c.block_on(future_with_timeout(Duration::from_millis(20), fut));
assert_eq!(result, Err(FutureWithTimeoutError));
let fut = future::ready(());
let result = c.block_on(future_with_timeout(Duration::from_millis(20), fut));
assert_eq!(result, Ok(()));
}
#[test]
fn test_future_with_timeout_send() {
let c = MainContext::new();
let l = MainLoop::new(Some(&c), false);
let l_clone = l.clone();
let fut = future::pending::<()>();
c.spawn(
future_with_timeout(Duration::from_millis(20), fut).then(move |result| {
l_clone.quit();
assert_eq!(result, Err(FutureWithTimeoutError));
futures_util::future::ready(())
}),
);
l.run();
let l_clone = l.clone();
let fut = future::ready(());
c.spawn(
future_with_timeout(Duration::from_millis(20), fut).then(move |result| {
l_clone.quit();
assert_eq!(result, Ok(()));
futures_util::future::ready(())
}),
);
l.run();
}
}
@@ -0,0 +1,59 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
use crate::{translate::*, BindingFlags};
use std::fmt;
crate::wrapper! {
#[doc(alias = "GBinding")]
pub struct Binding(Object<gobject_ffi::GBinding>);
match fn {
type_ => || gobject_ffi::g_binding_get_type(),
}
}
impl Binding {
#[doc(alias = "g_binding_get_flags")]
#[doc(alias = "get_flags")]
pub fn flags(&self) -> BindingFlags {
unsafe { from_glib(gobject_ffi::g_binding_get_flags(self.to_glib_none().0)) }
}
#[doc(alias = "g_binding_get_source_property")]
#[doc(alias = "get_source_property")]
pub fn source_property(&self) -> crate::GString {
unsafe {
from_glib_none(gobject_ffi::g_binding_get_source_property(
self.to_glib_none().0,
))
}
}
#[doc(alias = "g_binding_get_target_property")]
#[doc(alias = "get_target_property")]
pub fn target_property(&self) -> crate::GString {
unsafe {
from_glib_none(gobject_ffi::g_binding_get_target_property(
self.to_glib_none().0,
))
}
}
#[doc(alias = "g_binding_unbind")]
pub fn unbind(&self) {
unsafe {
gobject_ffi::g_binding_unbind(self.to_glib_none().0);
}
}
}
unsafe impl Send for Binding {}
unsafe impl Sync for Binding {}
impl fmt::Display for Binding {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Binding")
}
}
@@ -0,0 +1,94 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
use crate::{
prelude::*,
signal::{connect_raw, SignalHandlerId},
translate::*,
Object,
};
use std::{boxed::Box as Box_, fmt, mem::transmute};
crate::wrapper! {
#[doc(alias = "GBindingGroup")]
pub struct BindingGroup(Object<gobject_ffi::GBindingGroup>);
match fn {
type_ => || gobject_ffi::g_binding_group_get_type(),
}
}
impl BindingGroup {
#[doc(alias = "g_binding_group_new")]
pub fn new() -> BindingGroup {
unsafe { from_glib_full(gobject_ffi::g_binding_group_new()) }
}
#[doc(alias = "g_binding_group_dup_source")]
#[doc(alias = "dup_source")]
pub fn source(&self) -> Option<Object> {
unsafe {
from_glib_none(gobject_ffi::g_binding_group_dup_source(
self.to_glib_none().0,
))
}
}
#[doc(alias = "g_binding_group_set_source")]
pub fn set_source(&self, source: Option<&impl IsA<Object>>) {
unsafe {
gobject_ffi::g_binding_group_set_source(
self.to_glib_none().0,
source.map(|p| p.as_ref()).to_glib_none().0,
);
}
}
#[cfg(feature = "v2_72")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
#[doc(alias = "source")]
pub fn connect_source_notify<F: Fn(&Self) + Send + Sync + 'static>(
&self,
f: F,
) -> SignalHandlerId {
unsafe extern "C" fn notify_source_trampoline<
F: Fn(&BindingGroup) + Send + Sync + 'static,
>(
this: *mut crate::gobject_ffi::GBindingGroup,
_param_spec: ffi::gpointer,
f: ffi::gpointer,
) {
let f: &F = &*(f as *const F);
f(&from_glib_borrow(this))
}
unsafe {
let f: Box_<F> = Box_::new(f);
connect_raw(
self.as_ptr() as *mut _,
b"notify::source\0".as_ptr() as *const _,
Some(transmute::<_, unsafe extern "C" fn()>(
notify_source_trampoline::<F> as *const (),
)),
Box_::into_raw(f),
)
}
}
}
#[cfg(feature = "v2_72")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
impl Default for BindingGroup {
fn default() -> Self {
Self::new()
}
}
unsafe impl Send for BindingGroup {}
unsafe impl Sync for BindingGroup {}
impl fmt::Display for BindingGroup {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("BindingGroup")
}
}
@@ -0,0 +1,152 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
use crate::{bitflags::bitflags, prelude::*, translate::*};
use std::fmt;
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[doc(alias = "GBindingFlags")]
pub struct BindingFlags: u32 {
#[doc(alias = "G_BINDING_DEFAULT")]
const DEFAULT = gobject_ffi::G_BINDING_DEFAULT as _;
#[doc(alias = "G_BINDING_BIDIRECTIONAL")]
const BIDIRECTIONAL = gobject_ffi::G_BINDING_BIDIRECTIONAL as _;
#[doc(alias = "G_BINDING_SYNC_CREATE")]
const SYNC_CREATE = gobject_ffi::G_BINDING_SYNC_CREATE as _;
#[doc(alias = "G_BINDING_INVERT_BOOLEAN")]
const INVERT_BOOLEAN = gobject_ffi::G_BINDING_INVERT_BOOLEAN as _;
}
}
impl fmt::Display for BindingFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
#[doc(hidden)]
impl IntoGlib for BindingFlags {
type GlibType = gobject_ffi::GBindingFlags;
#[inline]
fn into_glib(self) -> gobject_ffi::GBindingFlags {
self.bits()
}
}
#[doc(hidden)]
impl FromGlib<gobject_ffi::GBindingFlags> for BindingFlags {
#[inline]
unsafe fn from_glib(value: gobject_ffi::GBindingFlags) -> Self {
Self::from_bits_truncate(value)
}
}
impl StaticType for BindingFlags {
#[inline]
#[doc(alias = "g_binding_flags_get_type")]
fn static_type() -> crate::Type {
unsafe { from_glib(gobject_ffi::g_binding_flags_get_type()) }
}
}
impl crate::HasParamSpec for BindingFlags {
type ParamSpec = crate::ParamSpecFlags;
type SetValue = Self;
type BuilderFn = fn(&str) -> crate::ParamSpecFlagsBuilder<Self>;
fn param_spec_builder() -> Self::BuilderFn {
Self::ParamSpec::builder
}
}
impl crate::value::ValueType for BindingFlags {
type Type = Self;
}
unsafe impl<'a> crate::value::FromValue<'a> for BindingFlags {
type Checker = crate::value::GenericValueTypeChecker<Self>;
#[inline]
unsafe fn from_value(value: &'a crate::Value) -> Self {
from_glib(crate::gobject_ffi::g_value_get_flags(
value.to_glib_none().0,
))
}
}
impl ToValue for BindingFlags {
#[inline]
fn to_value(&self) -> crate::Value {
let mut value = crate::Value::for_value_type::<Self>();
unsafe {
crate::gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, self.into_glib());
}
value
}
#[inline]
fn value_type(&self) -> crate::Type {
Self::static_type()
}
}
impl From<BindingFlags> for crate::Value {
#[inline]
fn from(v: BindingFlags) -> Self {
ToValue::to_value(&v)
}
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[doc(alias = "GSignalFlags")]
pub struct SignalFlags: u32 {
#[doc(alias = "G_SIGNAL_RUN_FIRST")]
const RUN_FIRST = gobject_ffi::G_SIGNAL_RUN_FIRST as _;
#[doc(alias = "G_SIGNAL_RUN_LAST")]
const RUN_LAST = gobject_ffi::G_SIGNAL_RUN_LAST as _;
#[doc(alias = "G_SIGNAL_RUN_CLEANUP")]
const RUN_CLEANUP = gobject_ffi::G_SIGNAL_RUN_CLEANUP as _;
#[doc(alias = "G_SIGNAL_NO_RECURSE")]
const NO_RECURSE = gobject_ffi::G_SIGNAL_NO_RECURSE as _;
#[doc(alias = "G_SIGNAL_DETAILED")]
const DETAILED = gobject_ffi::G_SIGNAL_DETAILED as _;
#[doc(alias = "G_SIGNAL_ACTION")]
const ACTION = gobject_ffi::G_SIGNAL_ACTION as _;
#[doc(alias = "G_SIGNAL_NO_HOOKS")]
const NO_HOOKS = gobject_ffi::G_SIGNAL_NO_HOOKS as _;
#[doc(alias = "G_SIGNAL_MUST_COLLECT")]
const MUST_COLLECT = gobject_ffi::G_SIGNAL_MUST_COLLECT as _;
#[doc(alias = "G_SIGNAL_DEPRECATED")]
const DEPRECATED = gobject_ffi::G_SIGNAL_DEPRECATED as _;
#[doc(alias = "G_SIGNAL_ACCUMULATOR_FIRST_RUN")]
const ACCUMULATOR_FIRST_RUN = gobject_ffi::G_SIGNAL_ACCUMULATOR_FIRST_RUN as _;
}
}
impl fmt::Display for SignalFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}
#[doc(hidden)]
impl IntoGlib for SignalFlags {
type GlibType = gobject_ffi::GSignalFlags;
#[inline]
fn into_glib(self) -> gobject_ffi::GSignalFlags {
self.bits()
}
}
#[doc(hidden)]
impl FromGlib<gobject_ffi::GSignalFlags> for SignalFlags {
#[inline]
unsafe fn from_glib(value: gobject_ffi::GSignalFlags) -> Self {
Self::from_bits_truncate(value)
}
}
@@ -0,0 +1,24 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
mod binding;
pub use self::binding::Binding;
#[cfg(feature = "v2_72")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
mod binding_group;
#[cfg(feature = "v2_72")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
pub use self::binding_group::BindingGroup;
#[cfg(feature = "v2_74")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
mod signal_group;
#[cfg(feature = "v2_74")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
pub use self::signal_group::SignalGroup;
mod flags;
pub use self::flags::BindingFlags;
pub use self::flags::SignalFlags;
@@ -0,0 +1,73 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
use crate::{prelude::*, translate::*, Object};
use std::fmt;
crate::wrapper! {
#[doc(alias = "GSignalGroup")]
pub struct SignalGroup(Object<gobject_ffi::GSignalGroup>);
match fn {
type_ => || gobject_ffi::g_signal_group_get_type(),
}
}
impl SignalGroup {
#[doc(alias = "g_signal_group_new")]
#[doc(alias = "new")]
pub fn with_type(target_type: crate::types::Type) -> SignalGroup {
unsafe { from_glib_full(gobject_ffi::g_signal_group_new(target_type.into_glib())) }
}
#[doc(alias = "g_signal_group_block")]
pub fn block(&self) {
unsafe {
gobject_ffi::g_signal_group_block(self.to_glib_none().0);
}
}
#[doc(alias = "g_signal_group_dup_target")]
#[doc(alias = "dup_target")]
pub fn target(&self) -> Option<Object> {
unsafe {
from_glib_full(gobject_ffi::g_signal_group_dup_target(
self.to_glib_none().0,
))
}
}
#[doc(alias = "g_signal_group_set_target")]
pub fn set_target(&self, target: Option<&impl IsA<Object>>) {
unsafe {
gobject_ffi::g_signal_group_set_target(
self.to_glib_none().0,
target.map(|p| p.as_ref()).to_glib_none().0,
);
}
}
#[doc(alias = "g_signal_group_unblock")]
pub fn unblock(&self) {
unsafe {
gobject_ffi::g_signal_group_unblock(self.to_glib_none().0);
}
}
#[cfg(feature = "v2_72")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
#[doc(alias = "target-type")]
pub fn target_type(&self) -> crate::types::Type {
ObjectExt::property(self, "target-type")
}
}
unsafe impl Send for SignalGroup {}
unsafe impl Sync for SignalGroup {}
impl fmt::Display for SignalGroup {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("SignalGroup")
}
}
@@ -0,0 +1,307 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use crate::{prelude::*, Binding, Object};
impl Binding {
#[doc(alias = "get_source")]
pub fn source(&self) -> Option<Object> {
self.property("source")
}
#[doc(alias = "get_target")]
pub fn target(&self) -> Option<Object> {
self.property("target")
}
}
#[cfg(test)]
mod test {
use crate::{prelude::*, subclass::prelude::*};
#[test]
fn binding() {
let source = TestObject::default();
let target = TestObject::default();
assert!(source.find_property("name").is_some());
source
.bind_property("name", &target, "name")
.bidirectional()
.build();
source.set_name("test_source_name");
assert_eq!(source.name(), target.name());
target.set_name("test_target_name");
assert_eq!(source.name(), target.name());
}
#[test]
fn binding_to_transform_with_values() {
let source = TestObject::default();
let target = TestObject::default();
source
.bind_property("name", &target, "name")
.sync_create()
.transform_to_with_values(|_binding, value| {
let value = value.get::<&str>().unwrap();
Some(format!("{value} World").to_value())
})
.transform_from_with_values(|_binding, value| {
let value = value.get::<&str>().unwrap();
Some(format!("{value} World").to_value())
})
.build();
source.set_name("Hello");
assert_eq!(target.name(), "Hello World");
}
#[test]
fn binding_from_transform_with_values() {
let source = TestObject::default();
let target = TestObject::default();
source
.bind_property("name", &target, "name")
.sync_create()
.bidirectional()
.transform_to_with_values(|_binding, value| {
let value = value.get::<&str>().unwrap();
Some(format!("{value} World").to_value())
})
.transform_from_with_values(|_binding, value| {
let value = value.get::<&str>().unwrap();
Some(format!("{value} World").to_value())
})
.build();
target.set_name("Hello");
assert_eq!(source.name(), "Hello World");
}
#[test]
fn binding_to_transform_ref() {
let source = TestObject::default();
let target = TestObject::default();
source
.bind_property("name", &target, "name")
.sync_create()
.transform_to(|_binding, value: &str| Some(format!("{value} World")))
.transform_from(|_binding, value: &str| Some(format!("{value} World")))
.build();
source.set_name("Hello");
assert_eq!(target.name(), "Hello World");
}
#[test]
fn binding_to_transform_owned_ref() {
let source = TestObject::default();
let target = TestObject::default();
source
.bind_property("name", &target, "name")
.sync_create()
.transform_to(|_binding, value: String| Some(format!("{value} World")))
.transform_from(|_binding, value: &str| Some(format!("{value} World")))
.build();
source.set_name("Hello");
assert_eq!(target.name(), "Hello World");
}
#[test]
fn binding_from_transform() {
let source = TestObject::default();
let target = TestObject::default();
source
.bind_property("name", &target, "name")
.sync_create()
.bidirectional()
.transform_to(|_binding, value: &str| Some(format!("{value} World")))
.transform_from(|_binding, value: &str| Some(format!("{value} World")))
.build();
target.set_name("Hello");
assert_eq!(source.name(), "Hello World");
}
#[test]
fn binding_to_transform_with_values_change_type() {
let source = TestObject::default();
let target = TestObject::default();
source
.bind_property("name", &target, "enabled")
.sync_create()
.transform_to_with_values(|_binding, value| {
let value = value.get::<&str>().unwrap();
Some((value == "Hello").to_value())
})
.transform_from_with_values(|_binding, value| {
let value = value.get::<bool>().unwrap();
Some((if value { "Hello" } else { "World" }).to_value())
})
.build();
source.set_name("Hello");
assert!(target.enabled());
source.set_name("Hello World");
assert!(!target.enabled());
}
#[test]
fn binding_from_transform_values_change_type() {
let source = TestObject::default();
let target = TestObject::default();
source
.bind_property("name", &target, "enabled")
.sync_create()
.bidirectional()
.transform_to_with_values(|_binding, value| {
let value = value.get::<&str>().unwrap();
Some((value == "Hello").to_value())
})
.transform_from_with_values(|_binding, value| {
let value = value.get::<bool>().unwrap();
Some((if value { "Hello" } else { "World" }).to_value())
})
.build();
target.set_enabled(true);
assert_eq!(source.name(), "Hello");
target.set_enabled(false);
assert_eq!(source.name(), "World");
}
#[test]
fn binding_to_transform_change_type() {
let source = TestObject::default();
let target = TestObject::default();
source
.bind_property("name", &target, "enabled")
.sync_create()
.transform_to(|_binding, value: &str| Some(value == "Hello"))
.transform_from(|_binding, value: bool| Some(if value { "Hello" } else { "World" }))
.build();
source.set_name("Hello");
assert!(target.enabled());
source.set_name("Hello World");
assert!(!target.enabled());
}
#[test]
fn binding_from_transform_change_type() {
let source = TestObject::default();
let target = TestObject::default();
source
.bind_property("name", &target, "enabled")
.sync_create()
.bidirectional()
.transform_to(|_binding, value: &str| Some(value == "Hello"))
.transform_from(|_binding, value: bool| Some(if value { "Hello" } else { "World" }))
.build();
target.set_enabled(true);
assert_eq!(source.name(), "Hello");
target.set_enabled(false);
assert_eq!(source.name(), "World");
}
mod imp {
use std::cell::RefCell;
use once_cell::sync::Lazy;
use super::*;
use crate as glib;
#[derive(Debug, Default)]
pub struct TestObject {
pub name: RefCell<String>,
pub enabled: RefCell<bool>,
}
#[crate::object_subclass]
impl ObjectSubclass for TestObject {
const NAME: &'static str = "TestBinding";
type Type = super::TestObject;
}
impl ObjectImpl for TestObject {
fn properties() -> &'static [crate::ParamSpec] {
static PROPERTIES: Lazy<Vec<crate::ParamSpec>> = Lazy::new(|| {
vec![
crate::ParamSpecString::builder("name")
.explicit_notify()
.build(),
crate::ParamSpecBoolean::builder("enabled")
.explicit_notify()
.build(),
]
});
PROPERTIES.as_ref()
}
fn property(&self, _id: usize, pspec: &crate::ParamSpec) -> crate::Value {
let obj = self.obj();
match pspec.name() {
"name" => obj.name().to_value(),
"enabled" => obj.enabled().to_value(),
_ => unimplemented!(),
}
}
fn set_property(&self, _id: usize, value: &crate::Value, pspec: &crate::ParamSpec) {
let obj = self.obj();
match pspec.name() {
"name" => obj.set_name(value.get().unwrap()),
"enabled" => obj.set_enabled(value.get().unwrap()),
_ => unimplemented!(),
};
}
}
}
crate::wrapper! {
pub struct TestObject(ObjectSubclass<imp::TestObject>);
}
impl Default for TestObject {
fn default() -> Self {
crate::Object::new()
}
}
impl TestObject {
fn name(&self) -> String {
self.imp().name.borrow().clone()
}
fn set_name(&self, name: &str) {
if name != self.imp().name.replace(name.to_string()).as_str() {
self.notify("name");
}
}
fn enabled(&self) -> bool {
*self.imp().enabled.borrow()
}
fn set_enabled(&self, enabled: bool) {
if enabled != self.imp().enabled.replace(enabled) {
self.notify("enabled");
}
}
}
}
@@ -0,0 +1,507 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{fmt, ptr};
use crate::{
object::ObjectRef, prelude::*, translate::*, Binding, BindingFlags, BindingGroup, BoolError,
Object, ParamSpec, Value,
};
impl BindingGroup {
#[doc(alias = "bind_with_closures")]
pub fn bind<'a, O: ObjectType>(
&'a self,
source_property: &'a str,
target: &'a O,
target_property: &'a str,
) -> BindingGroupBuilder<'a> {
BindingGroupBuilder::new(self, source_property, target, target_property)
}
}
type TransformFn = Option<Box<dyn Fn(&Binding, &Value) -> Option<Value> + Send + Sync + 'static>>;
// rustdoc-stripper-ignore-next
/// Builder for binding group bindings.
#[must_use = "The builder must be built to be used"]
pub struct BindingGroupBuilder<'a> {
group: &'a BindingGroup,
source_property: &'a str,
target: &'a ObjectRef,
target_property: &'a str,
flags: BindingFlags,
transform_to: TransformFn,
transform_from: TransformFn,
}
impl<'a> fmt::Debug for BindingGroupBuilder<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BindingGroupBuilder")
.field("group", &self.group)
.field("source_property", &self.source_property)
.field("target", &self.target)
.field("target_property", &self.target_property)
.field("flags", &self.flags)
.finish()
}
}
impl<'a> BindingGroupBuilder<'a> {
fn new(
group: &'a BindingGroup,
source_property: &'a str,
target: &'a impl ObjectType,
target_property: &'a str,
) -> Self {
Self {
group,
source_property,
target: target.as_object_ref(),
target_property,
flags: BindingFlags::DEFAULT,
transform_to: None,
transform_from: None,
}
}
// rustdoc-stripper-ignore-next
/// Transform changed property values from the target object to the source object with the given closure.
pub fn transform_from<F: Fn(&Binding, &Value) -> Option<Value> + Send + Sync + 'static>(
self,
func: F,
) -> Self {
Self {
transform_from: Some(Box::new(func)),
..self
}
}
// rustdoc-stripper-ignore-next
/// Transform changed property values from the source object to the target object with the given closure.
pub fn transform_to<F: Fn(&Binding, &Value) -> Option<Value> + Send + Sync + 'static>(
self,
func: F,
) -> Self {
Self {
transform_to: Some(Box::new(func)),
..self
}
}
// rustdoc-stripper-ignore-next
/// Bind the properties with the given flags.
pub fn flags(self, flags: BindingFlags) -> Self {
Self { flags, ..self }
}
// rustdoc-stripper-ignore-next
/// Set the binding flags to [`BIDIRECTIONAL`][crate::BindingFlags::BIDIRECTIONAL].
pub fn bidirectional(mut self) -> Self {
self.flags |= crate::BindingFlags::BIDIRECTIONAL;
self
}
// rustdoc-stripper-ignore-next
/// Set the binding flags to [`SYNC_CREATE`][crate::BindingFlags::SYNC_CREATE].
pub fn sync_create(mut self) -> Self {
self.flags |= crate::BindingFlags::SYNC_CREATE;
self
}
// rustdoc-stripper-ignore-next
/// Set the binding flags to [`INVERT_BOOLEAN`][crate::BindingFlags::INVERT_BOOLEAN].
pub fn invert_boolean(mut self) -> Self {
self.flags |= crate::BindingFlags::INVERT_BOOLEAN;
self
}
// rustdoc-stripper-ignore-next
/// Establish the property binding.
///
/// This fails if the provided properties do not exist.
pub fn try_build(self) -> Result<(), BoolError> {
unsafe extern "C" fn transform_to_trampoline(
binding: *mut gobject_ffi::GBinding,
from_value: *const gobject_ffi::GValue,
to_value: *mut gobject_ffi::GValue,
user_data: ffi::gpointer,
) -> ffi::gboolean {
let transform_data =
&*(user_data as *const (TransformFn, TransformFn, String, ParamSpec));
match (transform_data.0.as_ref().unwrap())(
&from_glib_borrow(binding),
&*(from_value as *const Value),
) {
None => false,
Some(res) => {
assert!(
res.type_().is_a(transform_data.3.value_type()),
"Target property {} expected type {} but transform_to function returned {}",
transform_data.3.name(),
transform_data.3.value_type(),
res.type_()
);
*to_value = res.into_raw();
true
}
}
.into_glib()
}
unsafe extern "C" fn transform_from_trampoline(
binding: *mut gobject_ffi::GBinding,
from_value: *const gobject_ffi::GValue,
to_value: *mut gobject_ffi::GValue,
user_data: ffi::gpointer,
) -> ffi::gboolean {
let transform_data =
&*(user_data as *const (TransformFn, TransformFn, String, ParamSpec));
let binding = from_glib_borrow(binding);
match (transform_data.1.as_ref().unwrap())(
&binding,
&*(from_value as *const Value),
) {
None => false,
Some(res) => {
let pspec_name = transform_data.2.clone();
let source = binding.source().unwrap();
let pspec = source.find_property(&pspec_name);
assert!(pspec.is_some(), "Source object does not have a property {pspec_name}");
let pspec = pspec.unwrap();
assert!(
res.type_().is_a(pspec.value_type()),
"Source property {pspec_name} expected type {} but transform_from function returned {}",
pspec.value_type(),
res.type_()
);
*to_value = res.into_raw();
true
}
}
.into_glib()
}
unsafe extern "C" fn free_transform_data(data: ffi::gpointer) {
let _ = Box::from_raw(data as *mut (TransformFn, TransformFn, String, ParamSpec));
}
let mut _source_propery_name_cstr = None;
let source_property_name = if let Some(source) = self.group.source() {
let source_property = source.find_property(self.source_property).ok_or_else(|| {
bool_error!(
"Source property {} on type {} not found",
self.source_property,
source.type_()
)
})?;
// This is NUL-termianted from the C side
source_property.name().as_ptr()
} else {
// This is a Rust &str and needs to be NUL-terminated first
let source_propery_name = std::ffi::CString::new(self.source_property).unwrap();
let source_property_name_ptr = source_propery_name.as_ptr() as *const u8;
_source_propery_name_cstr = Some(source_propery_name);
source_property_name_ptr
};
unsafe {
let target: Object = from_glib_none(self.target.clone().to_glib_none().0);
let target_property = target.find_property(self.target_property).ok_or_else(|| {
bool_error!(
"Target property {} on type {} not found",
self.target_property,
target.type_()
)
})?;
let target_property_name = target_property.name().as_ptr();
let have_transform_to = self.transform_to.is_some();
let have_transform_from = self.transform_from.is_some();
let transform_data = if have_transform_to || have_transform_from {
Box::into_raw(Box::new((
self.transform_to,
self.transform_from,
String::from_glib_none(source_property_name as *const _),
target_property,
)))
} else {
ptr::null_mut()
};
gobject_ffi::g_binding_group_bind_full(
self.group.to_glib_none().0,
source_property_name as *const _,
target.to_glib_none().0,
target_property_name as *const _,
self.flags.into_glib(),
if have_transform_to {
Some(transform_to_trampoline)
} else {
None
},
if have_transform_from {
Some(transform_from_trampoline)
} else {
None
},
transform_data as ffi::gpointer,
if transform_data.is_null() {
None
} else {
Some(free_transform_data)
},
);
}
Ok(())
}
// rustdoc-stripper-ignore-next
/// Similar to `try_build` but panics instead of failing.
pub fn build(self) {
self.try_build().unwrap()
}
}
#[cfg(test)]
mod test {
use crate::{prelude::*, subclass::prelude::*};
#[test]
fn binding_without_source() {
let binding_group = crate::BindingGroup::new();
let source = TestObject::default();
let target = TestObject::default();
assert!(source.find_property("name").is_some());
binding_group
.bind("name", &target, "name")
.bidirectional()
.build();
binding_group.set_source(Some(&source));
source.set_name("test_source_name");
assert_eq!(source.name(), target.name());
target.set_name("test_target_name");
assert_eq!(source.name(), target.name());
}
#[test]
fn binding_with_source() {
let binding_group = crate::BindingGroup::new();
let source = TestObject::default();
let target = TestObject::default();
binding_group.set_source(Some(&source));
binding_group.bind("name", &target, "name").build();
source.set_name("test_source_name");
assert_eq!(source.name(), target.name());
}
#[test]
fn binding_to_transform() {
let binding_group = crate::BindingGroup::new();
let source = TestObject::default();
let target = TestObject::default();
binding_group.set_source(Some(&source));
binding_group
.bind("name", &target, "name")
.sync_create()
.transform_to(|_binding, value| {
let value = value.get::<&str>().unwrap();
Some(format!("{value} World").to_value())
})
.transform_from(|_binding, value| {
let value = value.get::<&str>().unwrap();
Some(format!("{value} World").to_value())
})
.build();
source.set_name("Hello");
assert_eq!(target.name(), "Hello World");
}
#[test]
fn binding_from_transform() {
let binding_group = crate::BindingGroup::new();
let source = TestObject::default();
let target = TestObject::default();
binding_group.set_source(Some(&source));
binding_group
.bind("name", &target, "name")
.sync_create()
.bidirectional()
.transform_to(|_binding, value| {
let value = value.get::<&str>().unwrap();
Some(format!("{value} World").to_value())
})
.transform_from(|_binding, value| {
let value = value.get::<&str>().unwrap();
Some(format!("{value} World").to_value())
})
.build();
target.set_name("Hello");
assert_eq!(source.name(), "Hello World");
}
#[test]
fn binding_to_transform_change_type() {
let binding_group = crate::BindingGroup::new();
let source = TestObject::default();
let target = TestObject::default();
binding_group.set_source(Some(&source));
binding_group
.bind("name", &target, "enabled")
.sync_create()
.transform_to(|_binding, value| {
let value = value.get::<&str>().unwrap();
Some((value == "Hello").to_value())
})
.transform_from(|_binding, value| {
let value = value.get::<bool>().unwrap();
Some((if value { "Hello" } else { "World" }).to_value())
})
.build();
source.set_name("Hello");
assert!(target.enabled());
source.set_name("Hello World");
assert!(!target.enabled());
}
#[test]
fn binding_from_transform_change_type() {
let binding_group = crate::BindingGroup::new();
let source = TestObject::default();
let target = TestObject::default();
binding_group.set_source(Some(&source));
binding_group
.bind("name", &target, "enabled")
.sync_create()
.bidirectional()
.transform_to(|_binding, value| {
let value = value.get::<&str>().unwrap();
Some((value == "Hello").to_value())
})
.transform_from(|_binding, value| {
let value = value.get::<bool>().unwrap();
Some((if value { "Hello" } else { "World" }).to_value())
})
.build();
target.set_enabled(true);
assert_eq!(source.name(), "Hello");
target.set_enabled(false);
assert_eq!(source.name(), "World");
}
mod imp {
use std::cell::RefCell;
use once_cell::sync::Lazy;
use super::*;
use crate as glib;
#[derive(Debug, Default)]
pub struct TestObject {
pub name: RefCell<String>,
pub enabled: RefCell<bool>,
}
#[crate::object_subclass]
impl ObjectSubclass for TestObject {
const NAME: &'static str = "TestBindingGroup";
type Type = super::TestObject;
}
impl ObjectImpl for TestObject {
fn properties() -> &'static [crate::ParamSpec] {
static PROPERTIES: Lazy<Vec<crate::ParamSpec>> = Lazy::new(|| {
vec![
crate::ParamSpecString::builder("name")
.explicit_notify()
.build(),
crate::ParamSpecBoolean::builder("enabled")
.explicit_notify()
.build(),
]
});
PROPERTIES.as_ref()
}
fn property(&self, _id: usize, pspec: &crate::ParamSpec) -> crate::Value {
let obj = self.obj();
match pspec.name() {
"name" => obj.name().to_value(),
"enabled" => obj.enabled().to_value(),
_ => unimplemented!(),
}
}
fn set_property(&self, _id: usize, value: &crate::Value, pspec: &crate::ParamSpec) {
let obj = self.obj();
match pspec.name() {
"name" => obj.set_name(value.get().unwrap()),
"enabled" => obj.set_enabled(value.get().unwrap()),
_ => unimplemented!(),
};
}
}
}
crate::wrapper! {
pub struct TestObject(ObjectSubclass<imp::TestObject>);
}
impl Default for TestObject {
fn default() -> Self {
crate::Object::new()
}
}
impl TestObject {
fn name(&self) -> String {
self.imp().name.borrow().clone()
}
fn set_name(&self, name: &str) {
if name != self.imp().name.replace(name.to_string()).as_str() {
self.notify("name");
}
}
fn enabled(&self) -> bool {
*self.imp().enabled.borrow()
}
fn set_enabled(&self, enabled: bool) {
if enabled != self.imp().enabled.replace(enabled) {
self.notify("enabled");
}
}
}
}
+59
View File
@@ -0,0 +1,59 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use crate::translate::*;
bitflags::bitflags! {
#[doc(alias = "GParamFlags")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ParamFlags: u32 {
#[doc(alias = "G_PARAM_READABLE")]
const READABLE = gobject_ffi::G_PARAM_READABLE as _;
#[doc(alias = "G_PARAM_WRITABLE")]
const WRITABLE = gobject_ffi::G_PARAM_WRITABLE as _;
#[doc(alias = "G_PARAM_READWRITE")]
const READWRITE = gobject_ffi::G_PARAM_READWRITE as _;
#[doc(alias = "G_PARAM_CONSTRUCT")]
const CONSTRUCT = gobject_ffi::G_PARAM_CONSTRUCT as _;
#[doc(alias = "G_PARAM_CONSTRUCT_ONLY")]
const CONSTRUCT_ONLY = gobject_ffi::G_PARAM_CONSTRUCT_ONLY as _;
#[doc(alias = "G_PARAM_LAX_VALIDATION")]
const LAX_VALIDATION = gobject_ffi::G_PARAM_LAX_VALIDATION as _;
const USER_0 = 256;
const USER_1 = 512;
const USER_2 = 1024;
const USER_3 = 2048;
const USER_4 = 4096;
const USER_5 = 8192;
const USER_6 = 16384;
const USER_7 = 32768;
const USER_8 = 65536;
#[doc(alias = "G_PARAM_EXPLICIT_NOTIFY")]
const EXPLICIT_NOTIFY = gobject_ffi::G_PARAM_EXPLICIT_NOTIFY as _;
#[doc(alias = "G_PARAM_DEPRECATED")]
const DEPRECATED = gobject_ffi::G_PARAM_DEPRECATED as _;
}
}
impl Default for ParamFlags {
fn default() -> Self {
ParamFlags::READWRITE
}
}
#[doc(hidden)]
impl IntoGlib for ParamFlags {
type GlibType = gobject_ffi::GParamFlags;
#[inline]
fn into_glib(self) -> gobject_ffi::GParamFlags {
self.bits()
}
}
#[doc(hidden)]
impl FromGlib<gobject_ffi::GParamFlags> for ParamFlags {
#[inline]
unsafe fn from_glib(value: gobject_ffi::GParamFlags) -> Self {
Self::from_bits_truncate(value)
}
}
+22
View File
@@ -0,0 +1,22 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! GObject bindings
#[allow(unused_imports)]
mod auto;
mod binding;
#[cfg(feature = "v2_72")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
mod binding_group;
mod flags;
#[cfg(feature = "v2_74")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
mod signal_group;
#[cfg(feature = "v2_72")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
pub use binding_group::BindingGroupBuilder;
pub use self::{auto::*, flags::*};
//pub use self::auto::functions::*;
@@ -0,0 +1,248 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::mem::transmute;
use crate::{
signal::{connect_raw, SignalHandlerId},
translate::*,
IsA, Object, ObjectType, RustClosure, SignalGroup, Value,
};
impl SignalGroup {
#[doc(alias = "g_signal_group_new")]
pub fn new<T: IsA<Object>>() -> Self {
Self::with_type(T::static_type())
}
#[doc(alias = "g_signal_group_connect_closure")]
pub fn connect_closure(&self, signal_name: &str, after: bool, closure: RustClosure) {
unsafe {
gobject_ffi::g_signal_group_connect_closure(
self.to_glib_none().0,
signal_name.to_glib_none().0,
closure.as_ref().to_glib_none().0,
after.into_glib(),
);
}
}
#[doc(alias = "g_signal_group_connect")]
#[inline]
pub fn connect<F>(&self, signal_name: &str, after: bool, callback: F)
where
F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static,
{
self.connect_closure(signal_name, after, RustClosure::new(callback));
}
// rustdoc-stripper-ignore-next
/// Like [`Self::connect`] but doesn't require a `Send+Sync` closure. Signal emission will
/// panic if the signal on the current target is emitted from a different thread from the
/// thread that connected the signal.
#[inline]
pub fn connect_local<F>(&self, signal_name: &str, after: bool, callback: F)
where
F: Fn(&[Value]) -> Option<Value> + 'static,
{
self.connect_closure(signal_name, after, RustClosure::new_local(callback));
}
#[inline]
pub fn connect_notify<F>(&self, name: Option<&str>, callback: F)
where
F: Fn(&crate::Object, &crate::ParamSpec) + Send + Sync + 'static,
{
let signal_name = if let Some(name) = name {
format!("notify::{name}")
} else {
"notify".into()
};
let closure = crate::RustClosure::new(move |values| {
let obj = values[0].get().unwrap();
let pspec = values[1].get().unwrap();
callback(obj, pspec);
None
});
self.connect_closure(&signal_name, false, closure);
}
#[inline]
pub fn connect_notify_local<F>(&self, name: Option<&str>, callback: F)
where
F: Fn(&crate::Object, &crate::ParamSpec) + 'static,
{
let signal_name = if let Some(name) = name {
format!("notify::{name}")
} else {
"notify".into()
};
let closure = crate::RustClosure::new_local(move |values| {
let obj = values[0].get().unwrap();
let pspec = values[1].get().unwrap();
callback(obj, pspec);
None
});
self.connect_closure(&signal_name, false, closure);
}
unsafe fn connect_bind_unsafe<F: Fn(&Self, &Object)>(&self, f: F) -> SignalHandlerId {
unsafe extern "C" fn bind_trampoline<F: Fn(&SignalGroup, &Object)>(
this: *mut crate::gobject_ffi::GSignalGroup,
instance: *mut crate::gobject_ffi::GObject,
f: ffi::gpointer,
) {
let f: &F = &*(f as *const F);
f(&from_glib_borrow(this), &from_glib_borrow(instance))
}
let f: Box<F> = Box::new(f);
connect_raw(
self.as_ptr() as *mut _,
b"bind\0".as_ptr() as *const _,
Some(transmute::<_, unsafe extern "C" fn()>(
bind_trampoline::<F> as *const (),
)),
Box::into_raw(f),
)
}
unsafe fn connect_unbind_unsafe<F: Fn(&Self)>(&self, f: F) -> SignalHandlerId {
unsafe extern "C" fn unbind_trampoline<F: Fn(&SignalGroup)>(
this: *mut crate::gobject_ffi::GSignalGroup,
f: ffi::gpointer,
) {
let f: &F = &*(f as *const F);
f(&from_glib_borrow(this))
}
let f: Box<F> = Box::new(f);
connect_raw(
self.as_ptr() as *mut _,
b"unbind\0".as_ptr() as *const _,
Some(transmute::<_, unsafe extern "C" fn()>(
unbind_trampoline::<F> as *const (),
)),
Box::into_raw(f),
)
}
#[doc(alias = "bind")]
pub fn connect_bind<F: Fn(&Self, &Object) + Send + Sync + 'static>(
&self,
f: F,
) -> SignalHandlerId {
unsafe { self.connect_bind_unsafe(f) }
}
// rustdoc-stripper-ignore-next
/// Like [`Self::connect_bind`] but doesn't require a `Send+Sync` closure. Signal emission will
/// panic if the signal is emitted from a different thread from the thread that connected the
/// signal.
pub fn connect_bind_local<F: Fn(&Self, &Object) + 'static>(&self, f: F) -> SignalHandlerId {
let f = crate::thread_guard::ThreadGuard::new(f);
unsafe {
self.connect_bind_unsafe(move |s, o| {
(f.get_ref())(s, o);
})
}
}
#[doc(alias = "unbind")]
pub fn connect_unbind<F: Fn(&Self) + Send + Sync + 'static>(&self, f: F) -> SignalHandlerId {
unsafe { self.connect_unbind_unsafe(f) }
}
// rustdoc-stripper-ignore-next
/// Like [`Self::connect_unbind`] but doesn't require a `Send+Sync` closure. Signal emission
/// will panic if the signal is emitted from a different thread from the thread that connected
/// the signal.
pub fn connect_unbind_local<F: Fn(&Self) + 'static>(&self, f: F) -> SignalHandlerId {
let f = crate::thread_guard::ThreadGuard::new(f);
unsafe {
self.connect_unbind_unsafe(move |s| {
(f.get_ref())(s);
})
}
}
}
#[cfg(test)]
mod tests {
use std::{cell::RefCell, rc::Rc};
use super::*;
use crate as glib;
use crate::{ObjectExt, StaticType};
mod imp {
use super::*;
use crate::subclass::{prelude::*, Signal};
#[derive(Default)]
pub struct SignalObject {}
#[glib::object_subclass]
impl ObjectSubclass for SignalObject {
const NAME: &'static str = "SignalObject";
type Type = super::SignalObject;
}
impl ObjectImpl for SignalObject {
fn signals() -> &'static [Signal] {
use once_cell::sync::Lazy;
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
vec![
Signal::builder("sig-with-args")
.param_types([u32::static_type(), String::static_type()])
.build(),
Signal::builder("sig-with-ret")
.return_type::<String>()
.build(),
]
});
SIGNALS.as_ref()
}
}
}
wrapper! {
pub struct SignalObject(ObjectSubclass<imp::SignalObject>);
}
#[test]
fn group_emit() {
let group = SignalGroup::new::<SignalObject>();
let obj = Object::new::<SignalObject>();
let store = Rc::new(RefCell::new(String::new()));
group.connect_closure(
"sig-with-args",
false,
glib::closure_local!(@watch obj, @strong store => move |o: &SignalObject, a: u32, b: &str| {
assert_eq!(o, obj);
store.replace(format!("a {a} b {b}"));
})
);
group.connect_closure(
"sig-with-ret",
false,
glib::closure_local!(@watch obj => move |o: &SignalObject| -> &'static crate::GStr {
assert_eq!(o, obj);
crate::gstr!("Hello")
}),
);
group.set_target(Some(&obj));
obj.emit_by_name::<()>("sig-with-args", &[&5u32, &"World"]);
assert_eq!(*store.borrow(), "a 5 b World");
let ret = obj.emit_by_name::<crate::GString>("sig-with-ret", &[]);
assert_eq!(ret, "Hello");
group.set_target(Object::NONE);
let ret = obj.emit_by_name::<Option<String>>("sig-with-ret", &[]);
assert_eq!(ret, None);
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,367 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{cmp, fmt, hash, mem, ops, ptr, slice, str};
use crate::{translate::*, GStr};
wrapper! {
// rustdoc-stripper-ignore-next
/// A mutable text buffer that grows automatically.
#[doc(alias = "GString")]
#[must_use = "The builder must be built to be used"]
pub struct GStringBuilder(BoxedInline<ffi::GString>);
match fn {
copy => |ptr| ffi::g_string_new_len((*ptr).str, (*ptr).len as isize),
free => |ptr| ffi::g_string_free(ptr, ffi::GTRUE),
init => |ptr| unsafe {
let inner = ffi::GString {
str: ffi::g_malloc(64) as *mut _,
len: 0,
allocated_len: 64,
};
ptr::write(inner.str, 0);
*ptr = inner;
},
copy_into => |dest, src| {
debug_assert!((*src).allocated_len > (*src).len);
let allocated_len = (*src).allocated_len;
let inner = ffi::GString {
str: ffi::g_malloc(allocated_len) as *mut _,
len: 0,
allocated_len,
};
// +1 to also copy the NUL-terminator
ptr::copy_nonoverlapping((*src).str, inner.str, (*src).len + 1);
*dest = inner;
},
clear => |ptr| {
ffi::g_free((*ptr).str as *mut _);
},
type_ => || ffi::g_gstring_get_type(),
}
}
unsafe impl Send for GStringBuilder {}
unsafe impl Sync for GStringBuilder {}
impl GStringBuilder {
#[doc(alias = "g_string_new_len")]
#[inline]
pub fn new<T: AsRef<str>>(data: T) -> GStringBuilder {
let data = data.as_ref();
assert!(data.len() < usize::MAX - 1);
unsafe {
let allocated_len = usize::next_power_of_two(std::cmp::max(data.len(), 64) + 1);
assert_ne!(allocated_len, 0);
let inner = ffi::GString {
str: ffi::g_malloc(allocated_len) as *mut _,
len: data.len(),
allocated_len,
};
if data.is_empty() {
ptr::write(inner.str, 0);
} else {
ptr::copy_nonoverlapping(data.as_ptr() as *const _, inner.str, data.len());
ptr::write(inner.str.add(data.len()), 0);
}
Self { inner }
}
}
#[doc(alias = "g_string_append")]
#[doc(alias = "g_string_append_len")]
#[inline]
pub fn append(&mut self, val: &str) {
unsafe {
ffi::g_string_append_len(
self.to_glib_none_mut().0,
val.as_ptr() as *const _,
val.len() as isize,
);
}
}
#[doc(alias = "g_string_prepend")]
#[doc(alias = "g_string_prepend_len")]
#[inline]
pub fn prepend(&mut self, val: &str) {
unsafe {
ffi::g_string_prepend_len(
self.to_glib_none_mut().0,
val.as_ptr() as *const _,
val.len() as isize,
);
}
}
#[doc(alias = "g_string_append_c")]
#[doc(alias = "g_string_append_unichar")]
#[inline]
pub fn append_c(&mut self, val: char) {
unsafe {
ffi::g_string_append_unichar(self.to_glib_none_mut().0, val.into_glib());
}
}
#[doc(alias = "g_string_prepend_c")]
#[doc(alias = "g_string_prepend_unichar")]
#[inline]
pub fn prepend_c(&mut self, val: char) {
unsafe {
ffi::g_string_prepend_unichar(self.to_glib_none_mut().0, val.into_glib());
}
}
// rustdoc-stripper-ignore-next
/// Returns `&[str]` slice.
#[inline]
pub fn as_str(&self) -> &str {
unsafe {
let ptr: *const u8 = self.inner.str as _;
let len: usize = self.inner.len;
if len == 0 {
return "";
}
let slice = slice::from_raw_parts(ptr, len);
std::str::from_utf8_unchecked(slice)
}
}
// rustdoc-stripper-ignore-next
/// Returns <code>&[GStr]</code> slice.
#[inline]
pub fn as_gstr(&self) -> &GStr {
unsafe {
let ptr: *const u8 = self.inner.str as _;
let len: usize = self.inner.len;
if len == 0 {
return Default::default();
}
let slice = slice::from_raw_parts(ptr, len + 1);
GStr::from_utf8_with_nul_unchecked(slice)
}
}
// rustdoc-stripper-ignore-next
/// Finalizes the builder, converting it to a [`GString`].
#[must_use = "String returned from the builder should probably be used"]
#[inline]
pub fn into_string(self) -> crate::GString {
unsafe {
let s = mem::ManuallyDrop::new(self);
crate::GString::from_ptr_and_len_unchecked(s.inner.str, s.inner.len)
}
}
}
impl Default for GStringBuilder {
// rustdoc-stripper-ignore-next
/// Creates a new empty string.
#[inline]
fn default() -> Self {
Self::new("")
}
}
impl fmt::Debug for GStringBuilder {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl fmt::Display for GStringBuilder {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl PartialEq for GStringBuilder {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_str() == other.as_str()
}
}
impl PartialEq<str> for GStringBuilder {
#[inline]
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl PartialEq<GStringBuilder> for str {
#[inline]
fn eq(&self, other: &GStringBuilder) -> bool {
self == other.as_str()
}
}
impl PartialEq<GStr> for GStringBuilder {
#[inline]
fn eq(&self, other: &GStr) -> bool {
self.as_gstr() == other
}
}
impl PartialEq<GStringBuilder> for GStr {
#[inline]
fn eq(&self, other: &GStringBuilder) -> bool {
self == other.as_gstr()
}
}
impl Eq for GStringBuilder {}
impl cmp::PartialOrd for GStringBuilder {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl cmp::PartialOrd<str> for GStringBuilder {
#[inline]
fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
Some(self.as_str().cmp(other))
}
}
impl cmp::PartialOrd<GStringBuilder> for str {
#[inline]
fn partial_cmp(&self, other: &GStringBuilder) -> Option<cmp::Ordering> {
Some(self.cmp(other.as_str()))
}
}
impl cmp::PartialOrd<GStr> for GStringBuilder {
#[inline]
fn partial_cmp(&self, other: &GStr) -> Option<cmp::Ordering> {
Some(self.as_gstr().cmp(other))
}
}
impl cmp::PartialOrd<GStringBuilder> for GStr {
#[inline]
fn partial_cmp(&self, other: &GStringBuilder) -> Option<cmp::Ordering> {
Some(self.cmp(other.as_gstr()))
}
}
impl cmp::Ord for GStringBuilder {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.as_str().cmp(other.as_str())
}
}
impl hash::Hash for GStringBuilder {
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher,
{
self.as_str().hash(state)
}
}
impl AsRef<[u8]> for GStringBuilder {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl AsRef<str> for GStringBuilder {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<GStr> for GStringBuilder {
#[inline]
fn as_ref(&self) -> &GStr {
self.as_gstr()
}
}
impl ops::Deref for GStringBuilder {
type Target = str;
#[inline]
fn deref(&self) -> &str {
self.as_str()
}
}
impl fmt::Write for GStringBuilder {
#[inline]
fn write_str(&mut self, s: &str) -> fmt::Result {
self.append(s);
Ok(())
}
#[inline]
fn write_char(&mut self, c: char) -> fmt::Result {
self.append_c(c);
Ok(())
}
}
#[cfg(test)]
mod tests {
#[test]
fn append() {
let mut s = crate::GStringBuilder::new("");
assert_eq!(&*s, "");
s.append("Hello");
s.append(" ");
s.append("there!");
assert_eq!(&*s, "Hello there!");
assert_eq!(s.into_string().as_str(), "Hello there!");
}
#[test]
fn prepend() {
let mut s = crate::GStringBuilder::new("456");
assert_eq!(&*s, "456");
s.prepend("123");
assert_eq!(&*s, "123456");
}
#[test]
fn default() {
let s1: crate::GStringBuilder = Default::default();
assert_eq!(&*s1, "");
}
#[test]
fn display() {
let s: crate::GStringBuilder = crate::GStringBuilder::new("This is a string.");
assert_eq!(&format!("{s}"), "This is a string.");
}
#[test]
fn eq() {
let a1 = crate::GStringBuilder::new("a");
let a2 = crate::GStringBuilder::new("a");
let b = crate::GStringBuilder::new("b");
assert_eq!(a1, a2);
assert_ne!(a1, b);
assert_ne!(a2, b);
}
#[test]
fn write() {
use std::fmt::Write;
let mut s = crate::GStringBuilder::default();
write!(&mut s, "bla bla {} bla", 123).unwrap();
assert_eq!(&*s, "bla bla 123 bla");
}
}
+282
View File
@@ -0,0 +1,282 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{mem, path, ptr};
use crate::{translate::*, Error, GString, GStringPtr, KeyFile, KeyFileFlags, PtrSlice};
impl KeyFile {
#[doc(alias = "g_key_file_save_to_file")]
pub fn save_to_file<T: AsRef<std::path::Path>>(&self, filename: T) -> Result<(), Error> {
unsafe {
let mut error = ptr::null_mut();
let _ = ffi::g_key_file_save_to_file(
self.to_glib_none().0,
filename.as_ref().to_glib_none().0,
&mut error,
);
if error.is_null() {
Ok(())
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_load_from_data_dirs")]
pub fn load_from_data_dirs<T: AsRef<std::path::Path>>(
&self,
file: T,
flags: KeyFileFlags,
) -> Result<path::PathBuf, Error> {
unsafe {
let mut error = ptr::null_mut();
let mut full_path: *mut libc::c_char = ptr::null_mut();
let _ = ffi::g_key_file_load_from_data_dirs(
self.to_glib_none().0,
file.as_ref().to_glib_none().0,
&mut full_path,
flags.into_glib(),
&mut error,
);
if error.is_null() {
let path: GString = from_glib_full(full_path);
Ok(path::PathBuf::from(&path))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_load_from_dirs")]
pub fn load_from_dirs<T: AsRef<std::path::Path>, U: AsRef<std::path::Path>>(
&self,
file: T,
search_dirs: &[U],
flags: KeyFileFlags,
) -> Result<path::PathBuf, Error> {
unsafe {
let search_dirs: Vec<&std::path::Path> =
search_dirs.iter().map(AsRef::as_ref).collect();
let mut error = ptr::null_mut();
let mut full_path: *mut libc::c_char = ptr::null_mut();
let _ = ffi::g_key_file_load_from_dirs(
self.to_glib_none().0,
file.as_ref().to_glib_none().0,
search_dirs.to_glib_none().0,
&mut full_path,
flags.into_glib(),
&mut error,
);
if error.is_null() {
let path: GString = from_glib_full(full_path);
Ok(path::PathBuf::from(&path))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_to_data")]
pub fn to_data(&self) -> GString {
unsafe {
let ret =
ffi::g_key_file_to_data(self.to_glib_none().0, ptr::null_mut(), ptr::null_mut());
from_glib_full(ret)
}
}
#[doc(alias = "g_key_file_get_groups")]
#[doc(alias = "get_groups")]
pub fn groups(&self) -> PtrSlice<GStringPtr> {
unsafe {
let mut length = mem::MaybeUninit::uninit();
let ret = ffi::g_key_file_get_groups(self.to_glib_none().0, length.as_mut_ptr());
FromGlibContainer::from_glib_full_num(ret, length.assume_init() as _)
}
}
#[doc(alias = "g_key_file_get_keys")]
#[doc(alias = "get_keys")]
pub fn keys(&self, group_name: &str) -> Result<PtrSlice<GStringPtr>, crate::Error> {
unsafe {
let mut length = mem::MaybeUninit::uninit();
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_get_keys(
self.to_glib_none().0,
group_name.to_glib_none().0,
length.as_mut_ptr(),
&mut error,
);
if error.is_null() {
Ok(FromGlibContainer::from_glib_full_num(
ret,
length.assume_init() as _,
))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_get_boolean")]
#[doc(alias = "get_boolean")]
pub fn boolean(&self, group_name: &str, key: &str) -> Result<bool, Error> {
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_get_boolean(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
&mut error,
);
if error.is_null() {
Ok(from_glib(ret))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_has_key")]
pub fn has_key(&self, group_name: &str, key: &str) -> Result<bool, Error> {
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_has_key(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
&mut error,
);
if error.is_null() {
Ok(from_glib(ret))
} else {
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_get_boolean_list")]
#[doc(alias = "get_boolean_list")]
pub fn boolean_list(&self, group_name: &str, key: &str) -> Result<Vec<bool>, Error> {
unsafe {
let mut length = mem::MaybeUninit::uninit();
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_get_boolean_list(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
length.as_mut_ptr(),
&mut error,
);
if !error.is_null() {
return Err(from_glib_full(error));
}
Ok(FromGlibContainer::from_glib_container_num(
ret,
length.assume_init() as _,
))
}
}
#[doc(alias = "g_key_file_get_string")]
#[doc(alias = "get_string")]
pub fn string(&self, group_name: &str, key: &str) -> Result<GString, Error> {
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_get_string(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
&mut error,
);
if error.is_null() {
Ok(from_glib_full(ret))
} else {
ffi::g_free(ret as *mut _);
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_get_string_list")]
#[doc(alias = "get_string_list")]
pub fn string_list(&self, group_name: &str, key: &str) -> Result<PtrSlice<GStringPtr>, Error> {
unsafe {
let mut length = mem::MaybeUninit::uninit();
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_get_string_list(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
length.as_mut_ptr(),
&mut error,
);
if error.is_null() {
Ok(FromGlibContainer::from_glib_full_num(
ret,
length.assume_init() as _,
))
} else {
ffi::g_strfreev(ret);
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_get_locale_string")]
#[doc(alias = "get_locale_string")]
pub fn locale_string(
&self,
group_name: &str,
key: &str,
locale: Option<&str>,
) -> Result<GString, Error> {
unsafe {
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_get_locale_string(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
locale.to_glib_none().0,
&mut error,
);
if error.is_null() {
Ok(from_glib_full(ret))
} else {
ffi::g_free(ret as *mut _);
Err(from_glib_full(error))
}
}
}
#[doc(alias = "g_key_file_get_locale_string_list")]
#[doc(alias = "get_locale_string_list")]
pub fn locale_string_list(
&self,
group_name: &str,
key: &str,
locale: Option<&str>,
) -> Result<PtrSlice<GStringPtr>, Error> {
unsafe {
let mut length = mem::MaybeUninit::uninit();
let mut error = ptr::null_mut();
let ret = ffi::g_key_file_get_locale_string_list(
self.to_glib_none().0,
group_name.to_glib_none().0,
key.to_glib_none().0,
locale.to_glib_none().0,
length.as_mut_ptr(),
&mut error,
);
if error.is_null() {
Ok(FromGlibContainer::from_glib_full_num(
ret,
length.assume_init() as _,
))
} else {
ffi::g_strfreev(ret);
Err(from_glib_full(error))
}
}
}
}
+231
View File
@@ -0,0 +1,231 @@
// Take a look at the license at the top of the repository in the LICENSE file.
#![cfg_attr(docsrs, feature(doc_cfg))]
#![allow(clippy::missing_safety_doc)]
#![allow(renamed_and_removed_lints)]
#![doc = include_str!("../README.md")]
pub use bitflags;
pub use ffi;
#[doc(hidden)]
pub use glib_macros::cstr_bytes;
pub use glib_macros::{
clone, closure, closure_local, derived_properties, flags, object_interface, object_subclass,
Boxed, Downgrade, Enum, ErrorDomain, Properties, SharedBoxed, ValueDelegate, Variant,
};
pub use gobject_ffi;
pub use once_cell;
pub use self::{
byte_array::ByteArray,
bytes::Bytes,
closure::{Closure, RustClosure},
enums::{EnumClass, EnumValue, FlagsBuilder, FlagsClass, FlagsValue, UserDirectory},
error::{BoolError, Error},
object::{
BorrowedObject, Cast, CastNone, Class, InitiallyUnowned, Interface, IsA, Object, ObjectExt,
ObjectType, SendWeakRef, WeakRef,
},
signal::{
signal_handler_block, signal_handler_disconnect, signal_handler_unblock,
signal_stop_emission_by_name, Propagation, SignalHandlerId,
},
types::{ILong, Pointer, StaticType, StaticTypeExt, Type, ULong},
value::{BoxedValue, SendValue, ToSendValue, ToValue, Value},
variant::{
FixedSizeVariantArray, FixedSizeVariantType, FromVariant, StaticVariantType, ToVariant,
Variant,
},
variant_dict::VariantDict,
variant_iter::{VariantIter, VariantStrIter},
variant_type::{VariantTy, VariantTyIterator, VariantType},
FileError,
};
// Hack for the time being to retrieve the current function's name as a string.
// Based on the stdext cratelicensed under the MIT license.
//
// Copyright (c) 2020 Igor Aleksanov
//
// Previous attempts to get such a macro into std:
// * https://github.com/rust-lang/rfcs/pull/466
// * https://github.com/rust-lang/rfcs/pull/1719
// * https://github.com/rust-lang/rfcs/issues/1743
// * https://github.com/rust-lang/rfcs/pull/2818
// * ...
// rustdoc-stripper-ignore-next
/// This macro returns the name of the enclosing function.
/// As the internal implementation is based on the [`std::any::type_name`], this macro derives
/// all the limitations of this function.
///
/// ## Examples
///
/// ```rust
/// mod bar {
/// pub fn sample_function() {
/// assert!(glib::function_name!().ends_with("bar::sample_function"));
/// }
/// }
///
/// bar::sample_function();
/// ```
///
/// [`std::any::type_name`]: https://doc.rust-lang.org/std/any/fn.type_name.html
#[macro_export]
macro_rules! function_name {
() => {{
// Okay, this is ugly, I get it. However, this is the best we can get on a stable rust.
fn f() {}
fn type_name_of<T>(_: T) -> &'static str {
std::any::type_name::<T>()
}
let name = type_name_of(f);
// `3` is the length of the `::f`.
&name[..name.len() - 3]
}};
}
pub mod clone;
#[macro_use]
pub mod wrapper;
#[macro_use]
pub mod boxed;
#[macro_use]
pub mod boxed_inline;
#[macro_use]
pub mod shared;
#[macro_use]
pub mod error;
#[macro_use]
pub mod object;
mod boxed_any_object;
pub use boxed_any_object::BoxedAnyObject;
mod exit_code;
pub use exit_code::ExitCode;
pub mod collections;
pub use collections::{
ptr_slice::IntoPtrSlice, strv::IntoStrV, List, PtrSlice, SList, Slice, StrV,
};
pub use self::auto::{functions::*, *};
#[allow(clippy::too_many_arguments)]
#[allow(clippy::type_complexity)]
#[allow(unused_imports)]
#[allow(non_upper_case_globals)]
mod auto;
pub use self::gobject::*;
mod gobject;
mod byte_array;
mod bytes;
mod control_flow;
pub use self::control_flow::ControlFlow;
pub mod char;
pub use self::char::*;
mod checksum;
pub mod closure;
mod convert;
pub use self::convert::*;
mod enums;
mod manual_functions;
pub use self::manual_functions::*;
mod key_file;
pub mod prelude;
pub mod signal;
pub mod source;
pub use self::source::*;
#[macro_use]
pub mod translate;
mod gstring;
pub use self::gstring::*;
mod gstring_builder;
pub use self::gstring_builder::GStringBuilder;
pub mod types;
mod unicollate;
pub use self::unicollate::{CollationKey, FilenameCollationKey};
mod utils;
pub use self::utils::*;
mod unichar;
pub use self::unichar::*;
mod main_context;
mod main_context_channel;
pub use self::{
main_context::MainContextAcquireGuard,
main_context_channel::{Receiver, Sender, SyncSender},
};
mod date;
mod date_time;
mod time_span;
mod time_zone;
pub use self::time_span::*;
pub mod value;
pub mod variant;
mod variant_dict;
mod variant_iter;
mod variant_type;
pub use self::date::Date;
mod value_array;
pub use self::value_array::ValueArray;
mod param_spec;
pub use self::param_spec::*;
mod property;
pub use self::property::*;
mod quark;
pub use self::quark::Quark;
#[macro_use]
mod log;
#[doc(hidden)]
#[cfg(feature = "log_macros")]
#[cfg_attr(docsrs, doc(cfg(feature = "log_macros")))]
pub use rs_log;
pub use self::log::{
log_default_handler, log_remove_handler, log_set_always_fatal, log_set_default_handler,
log_set_fatal_mask, log_set_handler, log_set_writer_func, log_structured_array,
log_unset_default_handler, log_variant, log_writer_default, log_writer_format_fields,
log_writer_journald, log_writer_standard_streams, set_print_handler, set_printerr_handler,
unset_print_handler, unset_printerr_handler, LogField, LogHandlerId, LogLevel, LogLevels,
};
#[cfg(feature = "v2_68")]
pub use self::log::{log_writer_default_set_use_stderr, log_writer_default_would_drop};
#[cfg(unix)]
pub use self::log::{log_writer_is_journald, log_writer_supports_color};
#[cfg(feature = "log")]
#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
#[macro_use]
mod bridged_logging;
#[cfg(feature = "log")]
#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
pub use self::bridged_logging::{rust_log_handler, GlibLogger, GlibLoggerDomain, GlibLoggerFormat};
#[macro_use]
pub mod subclass;
mod main_context_futures;
pub use main_context_futures::{JoinError, JoinHandle, SpawnWithinJoinHandle};
mod source_futures;
pub use self::source_futures::*;
mod future_with_timeout;
pub use self::future_with_timeout::*;
mod thread_pool;
pub use self::thread_pool::{ThreadHandle, ThreadPool};
pub mod thread_guard;
// rustdoc-stripper-ignore-next
/// This is the log domain used by the [`clone!`][crate::clone!] macro. If you want to use a custom
/// logger (it prints to stdout by default), you can set your own logger using the corresponding
/// `log` functions.
pub const CLONE_MACRO_LOG_DOMAIN: &str = "glib-rs-clone";
#[cfg(target_family = "windows")]
mod win32;
#[cfg(target_family = "windows")]
pub use self::win32::*;
File diff suppressed because it is too large. Load diff
+279
View File
@@ -0,0 +1,279 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::mem;
use ffi::{self, gboolean, gpointer};
use crate::{source::Priority, translate::*, MainContext, Source, SourceId};
impl MainContext {
#[doc(alias = "g_main_context_prepare")]
pub fn prepare(&self) -> (bool, i32) {
unsafe {
let mut priority = mem::MaybeUninit::uninit();
let res = from_glib(ffi::g_main_context_prepare(
self.to_glib_none().0,
priority.as_mut_ptr(),
));
let priority = priority.assume_init();
(res, priority)
}
}
#[doc(alias = "g_main_context_find_source_by_id")]
pub fn find_source_by_id(&self, source_id: &SourceId) -> Option<Source> {
unsafe {
from_glib_none(ffi::g_main_context_find_source_by_id(
self.to_glib_none().0,
source_id.as_raw(),
))
}
}
// rustdoc-stripper-ignore-next
/// Invokes `func` on the main context.
///
/// If the current thread is the owner of the main context or the main context currently has no
/// owner then `func` will be called directly from inside this function. If this behaviour is
/// not desired and `func` should always be called asynchronously then use [`MainContext::spawn`]
/// [`glib::idle_add`](crate::idle_add) instead.
#[doc(alias = "g_main_context_invoke")]
pub fn invoke<F>(&self, func: F)
where
F: FnOnce() + Send + 'static,
{
self.invoke_with_priority(crate::Priority::DEFAULT_IDLE, func);
}
// rustdoc-stripper-ignore-next
/// Invokes `func` on the main context with the given priority.
///
/// If the current thread is the owner of the main context or the main context currently has no
/// owner then `func` will be called directly from inside this function. If this behaviour is
/// not desired and `func` should always be called asynchronously then use [`MainContext::spawn`]
/// [`glib::idle_add`](crate::idle_add) instead.
#[doc(alias = "g_main_context_invoke_full")]
pub fn invoke_with_priority<F>(&self, priority: Priority, func: F)
where
F: FnOnce() + Send + 'static,
{
unsafe {
self.invoke_unsafe(priority, func);
}
}
// rustdoc-stripper-ignore-next
/// Invokes `func` on the main context.
///
/// Different to `invoke()`, this does not require `func` to be
/// `Send` but can only be called from the thread that owns the main context.
///
/// This function panics if called from a different thread than the one that
/// owns the main context.
///
/// Note that this effectively means that `func` is called directly from inside this function
/// or otherwise panics immediately. If this behaviour is not desired and `func` should always
/// be called asynchronously then use [`MainContext::spawn_local`]
/// [`glib::idle_add_local`](crate::idle_add_local) instead.
pub fn invoke_local<F>(&self, func: F)
where
F: FnOnce() + 'static,
{
self.invoke_local_with_priority(crate::Priority::DEFAULT_IDLE, func);
}
// rustdoc-stripper-ignore-next
/// Invokes `func` on the main context with the given priority.
///
/// Different to `invoke_with_priority()`, this does not require `func` to be
/// `Send` but can only be called from the thread that owns the main context.
///
/// This function panics if called from a different thread than the one that
/// owns the main context.
///
/// Note that this effectively means that `func` is called directly from inside this function
/// or otherwise panics immediately. If this behaviour is not desired and `func` should always
/// be called asynchronously then use [`MainContext::spawn_local`]
/// [`glib::idle_add_local`](crate::idle_add_local) instead.
#[allow(clippy::if_same_then_else)]
pub fn invoke_local_with_priority<F>(&self, _priority: Priority, func: F)
where
F: FnOnce() + 'static,
{
// Checks from `g_main_context_invoke_full()`
// FIXME: Combine the first two cases somehow
if self.is_owner() {
func();
} else if let Ok(_acquire) = self.acquire() {
func();
} else {
panic!("Must be called from a thread that owns the main context");
}
}
unsafe fn invoke_unsafe<F>(&self, priority: Priority, func: F)
where
F: FnOnce() + 'static,
{
unsafe extern "C" fn trampoline<F: FnOnce() + 'static>(func: gpointer) -> gboolean {
let func: &mut Option<F> = &mut *(func as *mut Option<F>);
let func = func
.take()
.expect("MainContext::invoke() closure called multiple times");
func();
ffi::G_SOURCE_REMOVE
}
unsafe extern "C" fn destroy_closure<F: FnOnce() + 'static>(ptr: gpointer) {
let _ = Box::<Option<F>>::from_raw(ptr as *mut _);
}
let func = Box::into_raw(Box::new(Some(func)));
ffi::g_main_context_invoke_full(
self.to_glib_none().0,
priority.into_glib(),
Some(trampoline::<F>),
func as gpointer,
Some(destroy_closure::<F>),
)
}
// rustdoc-stripper-ignore-next
/// Call closure with the main context configured as the thread default one.
///
/// The thread default main context is changed in a panic-safe manner before calling `func` and
/// released again afterwards regardless of whether closure panicked or not.
///
/// This will fail if the main context is owned already by another thread.
#[doc(alias = "g_main_context_push_thread_default")]
pub fn with_thread_default<R, F: Sized>(&self, func: F) -> Result<R, crate::BoolError>
where
F: FnOnce() -> R,
{
let _acquire = self.acquire()?;
let _thread_default = ThreadDefaultContext::new(self);
Ok(func())
}
// rustdoc-stripper-ignore-next
/// Acquire ownership of the main context.
///
/// Ownership will automatically be released again once the returned acquire guard is dropped.
///
/// This will fail if the main context is owned already by another thread.
#[doc(alias = "g_main_context_acquire")]
pub fn acquire(&self) -> Result<MainContextAcquireGuard, crate::BoolError> {
unsafe {
let ret: bool = from_glib(ffi::g_main_context_acquire(self.to_glib_none().0));
if ret {
Ok(MainContextAcquireGuard(self))
} else {
Err(bool_error!("Failed to acquire ownership of main context, already acquired by another thread"))
}
}
}
}
#[must_use = "if unused the main context will be released immediately"]
pub struct MainContextAcquireGuard<'a>(&'a MainContext);
impl<'a> Drop for MainContextAcquireGuard<'a> {
#[doc(alias = "g_main_context_release")]
#[inline]
fn drop(&mut self) {
unsafe {
ffi::g_main_context_release(self.0.to_glib_none().0);
}
}
}
struct ThreadDefaultContext<'a>(&'a MainContext);
impl<'a> ThreadDefaultContext<'a> {
fn new(ctx: &MainContext) -> ThreadDefaultContext {
unsafe {
ffi::g_main_context_push_thread_default(ctx.to_glib_none().0);
}
ThreadDefaultContext(ctx)
}
}
impl<'a> Drop for ThreadDefaultContext<'a> {
#[inline]
fn drop(&mut self) {
unsafe {
ffi::g_main_context_pop_thread_default(self.0.to_glib_none().0);
}
}
}
#[cfg(test)]
mod tests {
use std::{panic, ptr, thread};
use super::*;
#[test]
fn test_invoke() {
let c = MainContext::new();
let l = crate::MainLoop::new(Some(&c), false);
let l_clone = l.clone();
thread::spawn(move || {
c.invoke(move || l_clone.quit());
});
l.run();
}
fn is_same_context(a: &MainContext, b: &MainContext) -> bool {
ptr::eq(a.to_glib_none().0, b.to_glib_none().0)
}
#[test]
fn test_with_thread_default() {
let a = MainContext::new();
let b = MainContext::new();
assert!(!is_same_context(&a, &b));
a.with_thread_default(|| {
let t = MainContext::thread_default().unwrap();
assert!(is_same_context(&a, &t));
b.with_thread_default(|| {
let t = MainContext::thread_default().unwrap();
assert!(is_same_context(&b, &t));
})
.unwrap();
let t = MainContext::thread_default().unwrap();
assert!(is_same_context(&a, &t));
})
.unwrap();
}
#[test]
fn test_with_thread_default_is_panic_safe() {
let a = MainContext::new();
let b = MainContext::new();
assert!(!is_same_context(&a, &b));
a.with_thread_default(|| {
let t = MainContext::thread_default().unwrap();
assert!(is_same_context(&a, &t));
let result = panic::catch_unwind(|| {
b.with_thread_default(|| {
panic!();
})
.unwrap();
});
assert!(result.is_err());
let t = MainContext::thread_default().unwrap();
assert!(is_same_context(&a, &t));
})
.unwrap();
}
}
@@ -0,0 +1,896 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{
collections::VecDeque,
fmt, mem, ptr,
sync::{mpsc, Arc, Condvar, Mutex},
};
use crate::{
thread_guard::ThreadGuard, translate::*, ControlFlow, MainContext, Priority, Source, SourceId,
};
enum ChannelSourceState {
NotAttached,
Attached(*mut ffi::GSource),
Destroyed,
}
unsafe impl Send for ChannelSourceState {}
unsafe impl Sync for ChannelSourceState {}
struct ChannelInner<T> {
queue: VecDeque<T>,
source: ChannelSourceState,
num_senders: usize,
}
impl<T> ChannelInner<T> {
fn receiver_disconnected(&self) -> bool {
match self.source {
ChannelSourceState::Destroyed => true,
// Receiver exists but is already destroyed
ChannelSourceState::Attached(source)
if unsafe { ffi::g_source_is_destroyed(source) } != ffi::GFALSE =>
{
true
}
// Not attached yet so the Receiver still exists
ChannelSourceState::NotAttached => false,
// Receiver still running
ChannelSourceState::Attached(_) => false,
}
}
#[doc(alias = "g_source_set_ready_time")]
fn set_ready_time(&mut self, ready_time: i64) {
if let ChannelSourceState::Attached(source) = self.source {
unsafe {
ffi::g_source_set_ready_time(source, ready_time);
}
}
}
}
struct ChannelBound {
bound: usize,
cond: Condvar,
}
struct Channel<T>(Arc<(Mutex<ChannelInner<T>>, Option<ChannelBound>)>);
impl<T> Clone for Channel<T> {
fn clone(&self) -> Channel<T> {
Channel(self.0.clone())
}
}
impl<T> Channel<T> {
fn new(bound: Option<usize>) -> Channel<T> {
Channel(Arc::new((
Mutex::new(ChannelInner {
queue: VecDeque::new(),
source: ChannelSourceState::NotAttached,
num_senders: 0,
}),
bound.map(|bound| ChannelBound {
bound,
cond: Condvar::new(),
}),
)))
}
fn send(&self, t: T) -> Result<(), mpsc::SendError<T>> {
let mut inner = (self.0).0.lock().unwrap();
// If we have a bounded channel then we need to wait here until enough free space is
// available or the receiver disappears
//
// A special case here is a bound of 0: the queue must be empty for accepting
// new data and then we will again wait later for the data to be actually taken
// out
if let Some(ChannelBound { bound, ref cond }) = (self.0).1 {
while inner.queue.len() >= bound
&& !inner.queue.is_empty()
&& !inner.receiver_disconnected()
{
inner = cond.wait(inner).unwrap();
}
}
// Error out directly if the receiver is disconnected
if inner.receiver_disconnected() {
return Err(mpsc::SendError(t));
}
// Store the item on our queue
inner.queue.push_back(t);
// and then wake up the GSource
inner.set_ready_time(0);
// If we have a bound of 0 we need to wait until the receiver actually
// handled the data
if let Some(ChannelBound { bound: 0, ref cond }) = (self.0).1 {
while !inner.queue.is_empty() && !inner.receiver_disconnected() {
inner = cond.wait(inner).unwrap();
}
// If the receiver was destroyed in the meantime take out the item and report an error
if inner.receiver_disconnected() {
// If the item is not in the queue anymore then the receiver just handled it before
// getting disconnected and all is good
if let Some(t) = inner.queue.pop_front() {
return Err(mpsc::SendError(t));
}
}
}
Ok(())
}
fn try_send(&self, t: T) -> Result<(), mpsc::TrySendError<T>> {
let mut inner = (self.0).0.lock().unwrap();
let ChannelBound { bound, ref cond } = (self.0)
.1
.as_ref()
.expect("called try_send() on an unbounded channel");
// Check if the queue is full and handle the special case of a 0 bound
if inner.queue.len() >= *bound && !inner.queue.is_empty() {
return Err(mpsc::TrySendError::Full(t));
}
// Error out directly if the receiver is disconnected
if inner.receiver_disconnected() {
return Err(mpsc::TrySendError::Disconnected(t));
}
// Store the item on our queue
inner.queue.push_back(t);
// and then wake up the GSource
inner.set_ready_time(0);
// If we have a bound of 0 we need to wait until the receiver actually
// handled the data
if *bound == 0 {
while !inner.queue.is_empty() && !inner.receiver_disconnected() {
inner = cond.wait(inner).unwrap();
}
// If the receiver was destroyed in the meantime take out the item and report an error
if inner.receiver_disconnected() {
// If the item is not in the queue anymore then the receiver just handled it before
// getting disconnected and all is good
if let Some(t) = inner.queue.pop_front() {
return Err(mpsc::TrySendError::Disconnected(t));
}
}
}
Ok(())
}
// SAFETY: Must be called from the main context the channel was attached to.
unsafe fn try_recv(&self) -> Result<T, mpsc::TryRecvError> {
let mut inner = (self.0).0.lock().unwrap();
// Pop item if we have any
if let Some(item) = inner.queue.pop_front() {
// Wake up a sender that is currently waiting, if any
if let Some(ChannelBound { ref cond, .. }) = (self.0).1 {
cond.notify_one();
}
return Ok(item);
}
// If there are no senders left we are disconnected or otherwise empty. That's the case if
// the only remaining strong reference is the one of the receiver
if inner.num_senders == 0 {
Err(mpsc::TryRecvError::Disconnected)
} else {
Err(mpsc::TryRecvError::Empty)
}
}
}
#[repr(C)]
struct ChannelSource<T, F: FnMut(T) -> ControlFlow + 'static> {
source: ffi::GSource,
source_funcs: Box<ffi::GSourceFuncs>,
channel: Channel<T>,
callback: ThreadGuard<F>,
}
unsafe extern "C" fn dispatch<T, F: FnMut(T) -> ControlFlow + 'static>(
source: *mut ffi::GSource,
callback: ffi::GSourceFunc,
_user_data: ffi::gpointer,
) -> ffi::gboolean {
let source = &mut *(source as *mut ChannelSource<T, F>);
debug_assert!(callback.is_none());
// Set ready-time to -1 so that we won't get called again before a new item is added
// to the channel queue.
ffi::g_source_set_ready_time(&mut source.source, -1);
// Get a reference to the callback. This will panic if we're called from a different
// thread than where the source was attached to the main context.
let callback = source.callback.get_mut();
// Now iterate over all items that we currently have in the channel until it is
// empty again. If all senders are disconnected at some point we remove the GSource
// from the main context it was attached to as it will never ever be called again.
loop {
match source.channel.try_recv() {
Err(mpsc::TryRecvError::Empty) => break,
Err(mpsc::TryRecvError::Disconnected) => return ffi::G_SOURCE_REMOVE,
Ok(item) => {
if callback(item).is_break() {
return ffi::G_SOURCE_REMOVE;
}
}
}
}
ffi::G_SOURCE_CONTINUE
}
#[cfg(feature = "v2_64")]
unsafe extern "C" fn dispose<T, F: FnMut(T) -> ControlFlow + 'static>(source: *mut ffi::GSource) {
let source = &mut *(source as *mut ChannelSource<T, F>);
// Set the source inside the channel to None so that all senders know that there
// is no receiver left and wake up the condition variable if any
let mut inner = (source.channel.0).0.lock().unwrap();
inner.source = ChannelSourceState::Destroyed;
if let Some(ChannelBound { ref cond, .. }) = (source.channel.0).1 {
cond.notify_all();
}
}
unsafe extern "C" fn finalize<T, F: FnMut(T) -> ControlFlow + 'static>(source: *mut ffi::GSource) {
let source = &mut *(source as *mut ChannelSource<T, F>);
// Drop all memory we own by taking it out of the Options
#[cfg(not(feature = "v2_64"))]
{
// FIXME: This is the same as would otherwise be done in the dispose() function but
// unfortunately it doesn't exist in older version of GLib. Doing it only here can
// cause a channel sender to get a reference to the source with reference count 0
// if it happens just before the mutex is taken below.
//
// This is exactly the pattern why g_source_set_dispose_function() was added.
//
// Set the source inside the channel to None so that all senders know that there
// is no receiver left and wake up the condition variable if any
let mut inner = (source.channel.0).0.lock().unwrap();
inner.source = ChannelSourceState::Destroyed;
if let Some(ChannelBound { ref cond, .. }) = (source.channel.0).1 {
cond.notify_all();
}
}
ptr::drop_in_place(&mut source.channel);
ptr::drop_in_place(&mut source.source_funcs);
// Take the callback out of the source. This will panic if the value is dropped
// from a different thread than where the callback was created so try to drop it
// from the main context if we're on another thread and the main context still exists.
//
// This can only really happen if the caller to `attach()` gets the `Source` from the returned
// `SourceId` and sends it to another thread or otherwise retrieves it from the main context,
// but better safe than sorry.
if source.callback.is_owner() {
ptr::drop_in_place(&mut source.callback);
} else {
let callback = ptr::read(&source.callback);
let context =
ffi::g_source_get_context(source as *mut ChannelSource<T, F> as *mut ffi::GSource);
if !context.is_null() {
let context = MainContext::from_glib_none(context);
context.invoke(move || {
drop(callback);
});
}
}
}
// rustdoc-stripper-ignore-next
/// A `Sender` that can be used to send items to the corresponding main context receiver.
///
/// This `Sender` behaves the same as `std::sync::mpsc::Sender`.
///
/// See [`MainContext::channel()`] for how to create such a `Sender`.
///
/// [`MainContext::channel()`]: struct.MainContext.html#method.channel
pub struct Sender<T>(Channel<T>);
// It's safe to send the Sender to other threads for attaching it as
// long as the items to be sent can also be sent between threads.
unsafe impl<T: Send> Send for Sender<T> {}
impl<T> fmt::Debug for Sender<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Sender").finish()
}
}
impl<T> Clone for Sender<T> {
fn clone(&self) -> Sender<T> {
Self::new(&self.0)
}
}
impl<T> Sender<T> {
fn new(channel: &Channel<T>) -> Self {
let mut inner = (channel.0).0.lock().unwrap();
inner.num_senders += 1;
Self(channel.clone())
}
// rustdoc-stripper-ignore-next
/// Sends a value to the channel.
pub fn send(&self, t: T) -> Result<(), mpsc::SendError<T>> {
self.0.send(t)
}
}
impl<T> Drop for Sender<T> {
fn drop(&mut self) {
// Decrease the number of senders and wake up the channel if this
// was the last sender that was dropped.
let mut inner = ((self.0).0).0.lock().unwrap();
inner.num_senders -= 1;
if inner.num_senders == 0 {
inner.set_ready_time(0);
}
}
}
// rustdoc-stripper-ignore-next
/// A `SyncSender` that can be used to send items to the corresponding main context receiver.
///
/// This `SyncSender` behaves the same as `std::sync::mpsc::SyncSender`.
///
/// See [`MainContext::sync_channel()`] for how to create such a `SyncSender`.
///
/// [`MainContext::sync_channel()`]: struct.MainContext.html#method.sync_channel
pub struct SyncSender<T>(Channel<T>);
// It's safe to send the SyncSender to other threads for attaching it as
// long as the items to be sent can also be sent between threads.
unsafe impl<T: Send> Send for SyncSender<T> {}
impl<T> fmt::Debug for SyncSender<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("SyncSender").finish()
}
}
impl<T> Clone for SyncSender<T> {
fn clone(&self) -> SyncSender<T> {
Self::new(&self.0)
}
}
impl<T> SyncSender<T> {
fn new(channel: &Channel<T>) -> Self {
let mut inner = (channel.0).0.lock().unwrap();
inner.num_senders += 1;
Self(channel.clone())
}
// rustdoc-stripper-ignore-next
/// Sends a value to the channel and blocks if the channel is full.
pub fn send(&self, t: T) -> Result<(), mpsc::SendError<T>> {
self.0.send(t)
}
// rustdoc-stripper-ignore-next
/// Sends a value to the channel.
pub fn try_send(&self, t: T) -> Result<(), mpsc::TrySendError<T>> {
self.0.try_send(t)
}
}
impl<T> Drop for SyncSender<T> {
fn drop(&mut self) {
// Decrease the number of senders and wake up the channel if this
// was the last sender that was dropped.
let mut inner = ((self.0).0).0.lock().unwrap();
inner.num_senders -= 1;
if inner.num_senders == 0 {
inner.set_ready_time(0);
}
}
}
// rustdoc-stripper-ignore-next
/// A `Receiver` that can be attached to a main context to receive items from its corresponding
/// `Sender` or `SyncSender`.
///
/// See [`MainContext::channel()`] or [`MainContext::sync_channel()`] for how to create
/// such a `Receiver`.
///
/// [`MainContext::channel()`]: struct.MainContext.html#method.channel
/// [`MainContext::sync_channel()`]: struct.MainContext.html#method.sync_channel
pub struct Receiver<T>(Option<Channel<T>>, Priority);
impl<T> fmt::Debug for Receiver<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Receiver").finish()
}
}
// It's safe to send the Receiver to other threads for attaching it as
// long as the items to be sent can also be sent between threads.
unsafe impl<T: Send> Send for Receiver<T> {}
impl<T> Drop for Receiver<T> {
fn drop(&mut self) {
// If the receiver was never attached to a main context we need to let all the senders know
if let Some(channel) = self.0.take() {
let mut inner = (channel.0).0.lock().unwrap();
inner.source = ChannelSourceState::Destroyed;
if let Some(ChannelBound { ref cond, .. }) = (channel.0).1 {
cond.notify_all();
}
}
}
}
impl<T> Receiver<T> {
// rustdoc-stripper-ignore-next
/// Attaches the receiver to the given `context` and calls `func` whenever an item is
/// available on the channel.
///
/// Passing `None` for the context will attach it to the thread default main context.
///
/// # Panics
///
/// This function panics if called from a thread that is not the owner of the provided
/// `context`, or, if `None` is provided, of the thread default main context.
pub fn attach<F: FnMut(T) -> ControlFlow + 'static>(
mut self,
context: Option<&MainContext>,
func: F,
) -> SourceId {
unsafe {
let channel = self.0.take().expect("Receiver without channel");
let source_funcs = Box::new(ffi::GSourceFuncs {
check: None,
prepare: None,
dispatch: Some(dispatch::<T, F>),
finalize: Some(finalize::<T, F>),
closure_callback: None,
closure_marshal: None,
});
let source = ffi::g_source_new(
mut_override(&*source_funcs),
mem::size_of::<ChannelSource<T, F>>() as u32,
) as *mut ChannelSource<T, F>;
#[cfg(feature = "v2_64")]
{
ffi::g_source_set_dispose_function(
source as *mut ffi::GSource,
Some(dispose::<T, F>),
);
}
// Set up the GSource
{
let source = &mut *source;
let mut inner = (channel.0).0.lock().unwrap();
ffi::g_source_set_priority(mut_override(&source.source), self.1.into_glib());
// We're immediately ready if the queue is not empty or if no sender is left at this point
ffi::g_source_set_ready_time(
mut_override(&source.source),
if !inner.queue.is_empty() || inner.num_senders == 0 {
0
} else {
-1
},
);
inner.source = ChannelSourceState::Attached(&mut source.source);
}
// Store all our data inside our part of the GSource
{
let source = &mut *source;
ptr::write(ptr::addr_of_mut!(source.channel), channel);
ptr::write(ptr::addr_of_mut!(source.callback), ThreadGuard::new(func));
ptr::write(ptr::addr_of_mut!(source.source_funcs), source_funcs);
}
let source = Source::from_glib_full(mut_override(&(*source).source));
let context = match context {
Some(context) => context.clone(),
None => MainContext::ref_thread_default(),
};
let _acquire = context
.acquire()
.expect("main context already acquired by another thread");
source.attach(Some(&context))
}
}
}
impl MainContext {
// rustdoc-stripper-ignore-next
/// Creates a channel for a main context.
///
/// The `Receiver` has to be attached to a main context at a later time, together with a
/// closure that will be called for every item sent to a `Sender`.
///
/// The `Sender` can be cloned and both the `Sender` and `Receiver` can be sent to different
/// threads as long as the item type implements the `Send` trait.
///
/// When the last `Sender` is dropped the channel is removed from the main context. If the
/// `Receiver` is dropped and not attached to a main context all sending to the `Sender`
/// will fail.
///
/// The returned `Sender` behaves the same as `std::sync::mpsc::Sender`.
#[deprecated = "Use an async channel, from async-channel for example, on the main context using spawn_future_local() instead"]
pub fn channel<T>(priority: Priority) -> (Sender<T>, Receiver<T>) {
let channel = Channel::new(None);
let receiver = Receiver(Some(channel.clone()), priority);
let sender = Sender::new(&channel);
(sender, receiver)
}
// rustdoc-stripper-ignore-next
/// Creates a synchronous channel for a main context with a given bound on the capacity of the
/// channel.
///
/// The `Receiver` has to be attached to a main context at a later time, together with a
/// closure that will be called for every item sent to a `SyncSender`.
///
/// The `SyncSender` can be cloned and both the `SyncSender` and `Receiver` can be sent to different
/// threads as long as the item type implements the `Send` trait.
///
/// When the last `SyncSender` is dropped the channel is removed from the main context. If the
/// `Receiver` is dropped and not attached to a main context all sending to the `SyncSender`
/// will fail.
///
/// The returned `SyncSender` behaves the same as `std::sync::mpsc::SyncSender`.
#[deprecated = "Use an async channel, from async-channel for example, on the main context using spawn_future_local() instead"]
pub fn sync_channel<T>(priority: Priority, bound: usize) -> (SyncSender<T>, Receiver<T>) {
let channel = Channel::new(Some(bound));
let receiver = Receiver(Some(channel.clone()), priority);
let sender = SyncSender::new(&channel);
(sender, receiver)
}
}
#[cfg(test)]
#[allow(deprecated)]
mod tests {
use std::{
cell::RefCell,
rc::Rc,
sync::atomic::{AtomicBool, Ordering},
thread, time,
};
use super::*;
use crate::MainLoop;
#[test]
fn test_channel() {
let c = MainContext::new();
let l = MainLoop::new(Some(&c), false);
let _guard = c.acquire().unwrap();
let (sender, receiver) = MainContext::channel(Priority::default());
let sum = Rc::new(RefCell::new(0));
let sum_clone = sum.clone();
let l_clone = l.clone();
receiver.attach(Some(&c), move |item| {
*sum_clone.borrow_mut() += item;
if *sum_clone.borrow() == 6 {
l_clone.quit();
ControlFlow::Break
} else {
ControlFlow::Continue
}
});
sender.send(1).unwrap();
sender.send(2).unwrap();
sender.send(3).unwrap();
l.run();
assert_eq!(*sum.borrow(), 6);
}
#[test]
fn test_drop_sender() {
let c = MainContext::new();
let l = MainLoop::new(Some(&c), false);
let _guard = c.acquire().unwrap();
let (sender, receiver) = MainContext::channel::<i32>(Priority::default());
struct Helper(MainLoop);
impl Drop for Helper {
fn drop(&mut self) {
self.0.quit();
}
}
let helper = Helper(l.clone());
receiver.attach(Some(&c), move |_| {
let _helper = &helper;
ControlFlow::Continue
});
drop(sender);
l.run();
}
#[test]
fn test_drop_receiver() {
let (sender, receiver) = MainContext::channel::<i32>(Priority::default());
drop(receiver);
assert_eq!(sender.send(1), Err(mpsc::SendError(1)));
}
#[test]
fn test_remove_receiver() {
let c = MainContext::new();
let _guard = c.acquire().unwrap();
let (sender, receiver) = MainContext::channel::<i32>(Priority::default());
let source_id = receiver.attach(Some(&c), move |_| ControlFlow::Continue);
let source = c.find_source_by_id(&source_id).unwrap();
source.destroy();
assert_eq!(sender.send(1), Err(mpsc::SendError(1)));
}
#[test]
fn test_remove_receiver_and_drop_source() {
let c = MainContext::new();
let _guard = c.acquire().unwrap();
let (sender, receiver) = MainContext::channel::<i32>(Priority::default());
struct Helper(Arc<AtomicBool>);
impl Drop for Helper {
fn drop(&mut self) {
self.0.store(true, Ordering::Relaxed);
}
}
let dropped = Arc::new(AtomicBool::new(false));
let helper = Helper(dropped.clone());
let source_id = receiver.attach(Some(&c), move |_| {
let _helper = &helper;
ControlFlow::Continue
});
let source = c.find_source_by_id(&source_id).unwrap();
source.destroy();
// This should drop the closure
drop(source);
assert!(dropped.load(Ordering::Relaxed));
assert_eq!(sender.send(1), Err(mpsc::SendError(1)));
}
#[test]
fn test_sync_channel() {
let c = MainContext::new();
let l = MainLoop::new(Some(&c), false);
let _guard = c.acquire().unwrap();
let (sender, receiver) = MainContext::sync_channel(Priority::default(), 2);
let sum = Rc::new(RefCell::new(0));
let sum_clone = sum.clone();
let l_clone = l.clone();
receiver.attach(Some(&c), move |item| {
*sum_clone.borrow_mut() += item;
if *sum_clone.borrow() == 6 {
l_clone.quit();
ControlFlow::Break
} else {
ControlFlow::Continue
}
});
let (wait_sender, wait_receiver) = mpsc::channel();
let thread = thread::spawn(move || {
// The first two must succeed
sender.try_send(1).unwrap();
sender.try_send(2).unwrap();
// This fill up the channel
assert!(sender.try_send(3).is_err());
wait_sender.send(()).unwrap();
// This will block
sender.send(3).unwrap();
});
// Wait until the channel is full, and then another
// 50ms to make sure the sender is blocked now and
// can wake up properly once an item was consumed
assert!(wait_receiver.recv().is_ok());
thread::sleep(time::Duration::from_millis(50));
l.run();
thread.join().unwrap();
assert_eq!(*sum.borrow(), 6);
}
#[test]
fn test_sync_channel_drop_wakeup() {
let c = MainContext::new();
let l = MainLoop::new(Some(&c), false);
let _guard = c.acquire().unwrap();
let (sender, receiver) = MainContext::sync_channel(Priority::default(), 3);
let sum = Rc::new(RefCell::new(0));
let sum_clone = sum.clone();
let l_clone = l.clone();
receiver.attach(Some(&c), move |item| {
*sum_clone.borrow_mut() += item;
if *sum_clone.borrow() == 6 {
l_clone.quit();
ControlFlow::Break
} else {
ControlFlow::Continue
}
});
let (wait_sender, wait_receiver) = mpsc::channel();
let thread = thread::spawn(move || {
// The first three must succeed
sender.try_send(1).unwrap();
sender.try_send(2).unwrap();
sender.try_send(3).unwrap();
wait_sender.send(()).unwrap();
for i in 4.. {
// This will block at some point until the
// receiver is removed from the main context
if sender.send(i).is_err() {
break;
}
}
});
// Wait until the channel is full, and then another
// 50ms to make sure the sender is blocked now and
// can wake up properly once an item was consumed
assert!(wait_receiver.recv().is_ok());
thread::sleep(time::Duration::from_millis(50));
l.run();
thread.join().unwrap();
assert_eq!(*sum.borrow(), 6);
}
#[test]
fn test_sync_channel_drop_receiver_wakeup() {
let c = MainContext::new();
let _guard = c.acquire().unwrap();
let (sender, receiver) = MainContext::sync_channel(Priority::default(), 2);
let (wait_sender, wait_receiver) = mpsc::channel();
let thread = thread::spawn(move || {
// The first two must succeed
sender.try_send(1).unwrap();
sender.try_send(2).unwrap();
wait_sender.send(()).unwrap();
// This will block and then error out because the receiver is destroyed
assert!(sender.send(3).is_err());
});
// Wait until the channel is full, and then another
// 50ms to make sure the sender is blocked now and
// can wake up properly once an item was consumed
assert!(wait_receiver.recv().is_ok());
thread::sleep(time::Duration::from_millis(50));
drop(receiver);
thread.join().unwrap();
}
#[test]
fn test_sync_channel_rendezvous() {
let c = MainContext::new();
let l = MainLoop::new(Some(&c), false);
let _guard = c.acquire().unwrap();
let (sender, receiver) = MainContext::sync_channel(Priority::default(), 0);
let (wait_sender, wait_receiver) = mpsc::channel();
let thread = thread::spawn(move || {
wait_sender.send(()).unwrap();
sender.send(1).unwrap();
wait_sender.send(()).unwrap();
sender.send(2).unwrap();
wait_sender.send(()).unwrap();
sender.send(3).unwrap();
wait_sender.send(()).unwrap();
});
// Wait until the thread is started, then wait another 50ms and
// during that time it must not have proceeded yet to send the
// second item because we did not yet receive the first item.
assert!(wait_receiver.recv().is_ok());
assert_eq!(
wait_receiver.recv_timeout(time::Duration::from_millis(50)),
Err(mpsc::RecvTimeoutError::Timeout)
);
let sum = Rc::new(RefCell::new(0));
let sum_clone = sum.clone();
let l_clone = l.clone();
receiver.attach(Some(&c), move |item| {
// We consumed one item so there should be one item on
// the other receiver now.
assert!(wait_receiver.recv().is_ok());
*sum_clone.borrow_mut() += item;
if *sum_clone.borrow() == 6 {
// But as we didn't consume the next one yet, there must be no
// other item available yet
assert_eq!(
wait_receiver.recv_timeout(time::Duration::from_millis(50)),
Err(mpsc::RecvTimeoutError::Disconnected)
);
l_clone.quit();
ControlFlow::Break
} else {
// But as we didn't consume the next one yet, there must be no
// other item available yet
assert_eq!(
wait_receiver.recv_timeout(time::Duration::from_millis(50)),
Err(mpsc::RecvTimeoutError::Timeout)
);
ControlFlow::Continue
}
});
l.run();
thread.join().unwrap();
assert_eq!(*sum.borrow(), 6);
}
}
@@ -0,0 +1,810 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{
any::Any, cell::Cell, marker::PhantomData, mem, num::NonZeroU32, panic, pin::Pin, ptr, thread,
};
use futures_channel::oneshot;
use futures_core::{
future::Future,
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
};
use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError};
use futures_util::FutureExt;
use crate::{
thread_guard::ThreadGuard, translate::*, MainContext, MainLoop, Priority, Source, SourceId,
};
// Wrapper around Send Futures and non-Send Futures that will panic
// if the non-Send Future is polled/dropped from a different thread
// than where this was created.
enum FutureWrapper {
Send(FutureObj<'static, Box<dyn Any + Send + 'static>>),
NonSend(ThreadGuard<LocalFutureObj<'static, Box<dyn Any + 'static>>>),
}
unsafe impl Send for FutureWrapper {}
unsafe impl Sync for FutureWrapper {}
impl Future for FutureWrapper {
type Output = Box<dyn Any + 'static>;
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
match self.get_mut() {
FutureWrapper::Send(fut) => {
Pin::new(fut).poll(ctx).map(|b| b as Box<dyn Any + 'static>)
}
FutureWrapper::NonSend(fut) => Pin::new(fut.get_mut()).poll(ctx),
}
}
}
// The TaskSource and WakerSource are split up as the TaskSource
// must only be finalized on the thread that owns the main context,
// but the WakerSource is passed around to arbitrary threads for
// being able to wake up the TaskSource.
//
// The WakerSource is set up as a child source of the TaskSource, i.e.
// whenever it is ready also the TaskSource is ready.
#[repr(C)]
struct TaskSource {
source: ffi::GSource,
future: FutureWrapper,
waker: Waker,
return_tx: Option<oneshot::Sender<thread::Result<Box<dyn Any + 'static>>>>,
}
#[repr(C)]
struct WakerSource {
source: ffi::GSource,
}
impl TaskSource {
unsafe extern "C" fn dispatch(
source: *mut ffi::GSource,
callback: ffi::GSourceFunc,
_user_data: ffi::gpointer,
) -> ffi::gboolean {
let source = &mut *(source as *mut Self);
debug_assert!(callback.is_none());
// Poll the TaskSource and ensure we're never called again if the
// contained Future resolved now.
if let Poll::Ready(()) = source.poll() {
ffi::G_SOURCE_REMOVE
} else {
ffi::G_SOURCE_CONTINUE
}
}
unsafe extern "C" fn finalize(source: *mut ffi::GSource) {
let source = source as *mut Self;
// This will panic if the future was a local future and is dropped from a different thread
// than where it was created so try to drop it from the main context if we're on another
// thread and the main context still exists.
//
// This can only really happen if the `Source` was manually retrieve from the context, but
// better safe than sorry.
match (*source).future {
FutureWrapper::Send(_) => {
ptr::drop_in_place(&mut (*source).future);
}
FutureWrapper::NonSend(ref mut future) if future.is_owner() => {
ptr::drop_in_place(&mut (*source).future);
}
FutureWrapper::NonSend(ref mut future) => {
let context = ffi::g_source_get_context(source as *mut ffi::GSource);
if !context.is_null() {
let future = ptr::read(future);
let context = MainContext::from_glib_none(context);
context.invoke(move || {
drop(future);
});
} else {
// This will panic
ptr::drop_in_place(&mut (*source).future);
}
}
}
// Drop the waker to unref the underlying GSource
ptr::drop_in_place(&mut (*source).waker);
}
}
impl WakerSource {
unsafe fn clone_raw(waker: *const ()) -> RawWaker {
static VTABLE: RawWakerVTable = RawWakerVTable::new(
WakerSource::clone_raw,
WakerSource::wake_raw,
WakerSource::wake_by_ref_raw,
WakerSource::drop_raw,
);
let waker = waker as *const ffi::GSource;
ffi::g_source_ref(mut_override(waker));
RawWaker::new(waker as *const (), &VTABLE)
}
unsafe fn wake_raw(waker: *const ()) {
Self::wake_by_ref_raw(waker);
Self::drop_raw(waker);
}
unsafe fn wake_by_ref_raw(waker: *const ()) {
let waker = waker as *const ffi::GSource;
ffi::g_source_set_ready_time(mut_override(waker), 0);
}
unsafe fn drop_raw(waker: *const ()) {
let waker = waker as *const ffi::GSource;
ffi::g_source_unref(mut_override(waker));
}
unsafe extern "C" fn dispatch(
source: *mut ffi::GSource,
_callback: ffi::GSourceFunc,
_user_data: ffi::gpointer,
) -> ffi::gboolean {
// Set ready-time to -1 so that we're not called again before
// being woken up another time.
ffi::g_source_set_ready_time(mut_override(source), -1);
ffi::G_SOURCE_CONTINUE
}
}
unsafe impl Send for TaskSource {}
unsafe impl Sync for TaskSource {}
unsafe impl Send for WakerSource {}
unsafe impl Sync for WakerSource {}
impl TaskSource {
#[allow(clippy::new_ret_no_self)]
// checker-ignore-item
fn new(
priority: Priority,
future: FutureWrapper,
return_tx: Option<oneshot::Sender<thread::Result<Box<dyn Any + 'static>>>>,
) -> Source {
unsafe {
static TASK_SOURCE_FUNCS: ffi::GSourceFuncs = ffi::GSourceFuncs {
check: None,
prepare: None,
dispatch: Some(TaskSource::dispatch),
finalize: Some(TaskSource::finalize),
closure_callback: None,
closure_marshal: None,
};
static WAKER_SOURCE_FUNCS: ffi::GSourceFuncs = ffi::GSourceFuncs {
check: None,
prepare: None,
dispatch: Some(WakerSource::dispatch),
finalize: None,
closure_callback: None,
closure_marshal: None,
};
let source = ffi::g_source_new(
mut_override(&TASK_SOURCE_FUNCS),
mem::size_of::<Self>() as u32,
);
let waker_source = ffi::g_source_new(
mut_override(&WAKER_SOURCE_FUNCS),
mem::size_of::<WakerSource>() as u32,
);
ffi::g_source_set_priority(source, priority.into_glib());
ffi::g_source_add_child_source(source, waker_source);
{
let source = &mut *(source as *mut Self);
ptr::write(&mut source.future, future);
ptr::write(&mut source.return_tx, return_tx);
// This creates a new reference to the waker source.
let waker = Waker::from_raw(WakerSource::clone_raw(waker_source as *const ()));
ptr::write(&mut source.waker, waker);
}
// Set ready time to 0 so that the source is immediately dispatched
// for doing the initial polling. This will then either resolve the
// future or register the waker wherever necessary.
ffi::g_source_set_ready_time(waker_source, 0);
// Unref the waker source, a strong reference to it is stored inside
// the task source directly and inside the task source as child source.
ffi::g_source_unref(waker_source);
from_glib_full(source)
}
}
fn poll(&mut self) -> Poll<()> {
let source = &self.source as *const _;
let executor: Borrowed<MainContext> =
unsafe { from_glib_borrow(ffi::g_source_get_context(mut_override(source))) };
assert!(
executor.is_owner(),
"Polling futures only allowed if the thread is owning the MainContext"
);
executor
.with_thread_default(|| {
let _enter = futures_executor::enter().unwrap();
let mut context = Context::from_waker(&self.waker);
// This will panic if the future was a local future and is called from
// a different thread than where it was created.
if let Some(tx) = self.return_tx.take() {
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
Pin::new(&mut self.future).poll(&mut context)
}));
match res {
Ok(Poll::Ready(res)) => {
let _ = tx.send(Ok(res));
Poll::Ready(())
}
Ok(Poll::Pending) => {
self.return_tx.replace(tx);
Poll::Pending
}
Err(e) => {
let _ = tx.send(Err(e));
Poll::Ready(())
}
}
} else {
Pin::new(&mut self.future).poll(&mut context).map(|_| ())
}
})
.expect("current thread is not owner of the main context")
}
}
// rustdoc-stripper-ignore-next
/// A handle to a task running on a [`MainContext`].
///
/// Like [`std::thread::JoinHandle`] for a task rather than a thread. The return value from the
/// task can be retrieved by awaiting on this object. Dropping the handle "detaches" the task,
/// allowing it to complete but discarding the return value.
#[derive(Debug)]
pub struct JoinHandle<T> {
rx: oneshot::Receiver<std::thread::Result<Box<dyn Any + 'static>>>,
source: Source,
id: Cell<Option<NonZeroU32>>,
phantom: PhantomData<oneshot::Receiver<std::thread::Result<T>>>,
}
impl<T> JoinHandle<T> {
#[inline]
fn new(
ctx: &MainContext,
source: Source,
rx: oneshot::Receiver<std::thread::Result<Box<dyn Any + 'static>>>,
) -> Self {
let id = source.attach(Some(ctx));
let id = Cell::new(Some(unsafe { NonZeroU32::new_unchecked(id.as_raw()) }));
Self {
rx,
source,
id,
phantom: PhantomData,
}
}
// rustdoc-stripper-ignore-next
/// Returns the internal source ID.
///
/// Returns `None` if the handle was aborted already.
#[inline]
pub fn as_raw_source_id(&self) -> Option<u32> {
self.id.get().map(|i| i.get())
}
// rustdoc-stripper-ignore-next
/// Aborts the task associated with the handle.
#[inline]
pub fn abort(&self) {
self.source.destroy();
self.id.replace(None);
}
// rustdoc-stripper-ignore-next
/// Returns the [`Source`] associated with this handle.
#[inline]
pub fn source(&self) -> &Source {
&self.source
}
// rustdoc-stripper-ignore-next
/// Safely converts the handle into a [`SourceId`].
///
/// Can be used to discard the return value while still retaining the ability to abort the
/// underlying task. Returns `Err(self)` if the handle was aborted already.
pub fn into_source_id(self) -> Result<SourceId, Self> {
if let Some(id) = self.id.take() {
Ok(unsafe { SourceId::from_glib(id.get()) })
} else {
Err(self)
}
}
}
impl<T: 'static> Future for JoinHandle<T> {
type Output = Result<T, JoinError>;
#[inline]
fn poll(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
std::pin::Pin::new(&mut self.rx).poll(cx).map(|r| match r {
Err(_) => Err(JoinErrorInner::Cancelled.into()),
Ok(Err(e)) => Err(JoinErrorInner::Panic(e).into()),
Ok(Ok(r)) => Ok(*r.downcast().unwrap()),
})
}
}
impl<T: 'static> futures_core::FusedFuture for JoinHandle<T> {
#[inline]
fn is_terminated(&self) -> bool {
self.rx.is_terminated()
}
}
unsafe impl<T> Send for JoinHandle<T> {}
// rustdoc-stripper-ignore-next
/// Variant of [`JoinHandle`] that is returned from [`MainContext::spawn_from_within`].
#[derive(Debug)]
pub struct SpawnWithinJoinHandle<T> {
rx: Option<oneshot::Receiver<JoinHandle<T>>>,
join_handle: Option<JoinHandle<T>>,
}
impl<T> SpawnWithinJoinHandle<T> {
// rustdoc-stripper-ignore-next
/// Waits until the task is spawned and returns the [`JoinHandle`].
pub async fn into_inner(self) -> Result<JoinHandle<T>, JoinError> {
if let Some(join_handle) = self.join_handle {
return Ok(join_handle);
}
if let Some(rx) = self.rx {
match rx.await {
Ok(join_handle) => return Ok(join_handle),
Err(_) => return Err(JoinErrorInner::Cancelled.into()),
}
}
Err(JoinErrorInner::Cancelled.into())
}
}
impl<T: 'static> Future for SpawnWithinJoinHandle<T> {
type Output = Result<T, JoinError>;
#[inline]
fn poll(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
if let Some(ref mut rx) = self.rx {
match std::pin::Pin::new(rx).poll(cx) {
std::task::Poll::Pending => return std::task::Poll::Pending,
std::task::Poll::Ready(Err(_)) => {
self.rx = None;
return std::task::Poll::Ready(Err(JoinErrorInner::Cancelled.into()));
}
std::task::Poll::Ready(Ok(join_handle)) => {
self.rx = None;
self.join_handle = Some(join_handle);
}
}
}
if let Some(ref mut join_handle) = self.join_handle {
match std::pin::Pin::new(join_handle).poll(cx) {
std::task::Poll::Pending => return std::task::Poll::Pending,
std::task::Poll::Ready(Err(e)) => {
self.join_handle = None;
return std::task::Poll::Ready(Err(e));
}
std::task::Poll::Ready(Ok(r)) => {
self.join_handle = None;
return std::task::Poll::Ready(Ok(r));
}
}
}
std::task::Poll::Ready(Err(JoinErrorInner::Cancelled.into()))
}
}
impl<T: 'static> futures_core::FusedFuture for SpawnWithinJoinHandle<T> {
#[inline]
fn is_terminated(&self) -> bool {
if let Some(ref rx) = self.rx {
rx.is_terminated()
} else if let Some(ref join_handle) = self.join_handle {
join_handle.is_terminated()
} else {
true
}
}
}
// rustdoc-stripper-ignore-next
/// Task failure from awaiting a [`JoinHandle`].
#[derive(Debug)]
pub struct JoinError(JoinErrorInner);
impl JoinError {
// rustdoc-stripper-ignore-next
/// Returns `true` if the handle was cancelled.
#[inline]
pub fn is_cancelled(&self) -> bool {
matches!(self.0, JoinErrorInner::Cancelled)
}
// rustdoc-stripper-ignore-next
/// Returns `true` if the task terminated with a panic.
#[inline]
pub fn is_panic(&self) -> bool {
matches!(self.0, JoinErrorInner::Panic(_))
}
// rustdoc-stripper-ignore-next
/// Converts the error into a panic result.
///
/// # Panics
///
/// Panics if the error is not a panic error. Use [`is_panic`](Self::is_panic) to check first
/// if the error represents a panic.
#[inline]
pub fn into_panic(self) -> Box<dyn Any + Send + 'static> {
self.try_into_panic()
.expect("`JoinError` is not a panic error")
}
// rustdoc-stripper-ignore-next
/// Attempts to convert the error into a panic result.
///
/// Returns `Err(self)` if the error is not a panic result.
#[inline]
pub fn try_into_panic(self) -> Result<Box<dyn Any + Send + 'static>, Self> {
match self.0 {
JoinErrorInner::Panic(e) => Ok(e),
e => Err(Self(e)),
}
}
}
impl std::error::Error for JoinError {}
impl std::fmt::Display for JoinError {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[derive(thiserror::Error, Debug)]
enum JoinErrorInner {
#[error("task cancelled")]
Cancelled,
#[error("task panicked")]
Panic(Box<dyn Any + Send + 'static>),
}
impl From<JoinErrorInner> for JoinError {
#[inline]
fn from(e: JoinErrorInner) -> Self {
Self(e)
}
}
impl MainContext {
// rustdoc-stripper-ignore-next
/// Spawn a new infallible `Future` on the main context.
///
/// This can be called from any thread and will execute the future from the thread
/// where main context is running, e.g. via a `MainLoop`.
pub fn spawn<R: Send + 'static, F: Future<Output = R> + Send + 'static>(
&self,
f: F,
) -> JoinHandle<R> {
self.spawn_with_priority(crate::Priority::default(), f)
}
// rustdoc-stripper-ignore-next
/// Spawn a new infallible `Future` on the main context.
///
/// The given `Future` does not have to be `Send`.
///
/// This can be called only from the thread where the main context is running, e.g.
/// from any other `Future` that is executed on this main context, or after calling
/// `with_thread_default` or `acquire` on the main context.
pub fn spawn_local<R: 'static, F: Future<Output = R> + 'static>(&self, f: F) -> JoinHandle<R> {
self.spawn_local_with_priority(crate::Priority::default(), f)
}
// rustdoc-stripper-ignore-next
/// Spawn a new infallible `Future` on the main context, with a non-default priority.
///
/// This can be called from any thread and will execute the future from the thread
/// where main context is running, e.g. via a `MainLoop`.
pub fn spawn_with_priority<R: Send + 'static, F: Future<Output = R> + Send + 'static>(
&self,
priority: Priority,
f: F,
) -> JoinHandle<R> {
let f = FutureObj::new(Box::new(async move {
Box::new(f.await) as Box<dyn Any + Send + 'static>
}));
let (tx, rx) = oneshot::channel();
let source = TaskSource::new(priority, FutureWrapper::Send(f), Some(tx));
JoinHandle::new(self, source, rx)
}
// rustdoc-stripper-ignore-next
/// Spawn a new infallible `Future` on the main context, with a non-default priority.
///
/// The given `Future` does not have to be `Send`.
///
/// This can be called only from the thread where the main context is running, e.g.
/// from any other `Future` that is executed on this main context, or after calling
/// `with_thread_default` or `acquire` on the main context.
pub fn spawn_local_with_priority<R: 'static, F: Future<Output = R> + 'static>(
&self,
priority: Priority,
f: F,
) -> JoinHandle<R> {
let _acquire = self
.acquire()
.expect("Spawning local futures only allowed on the thread owning the MainContext");
let f = LocalFutureObj::new(Box::new(async move {
Box::new(f.await) as Box<dyn Any + 'static>
}));
let (tx, rx) = oneshot::channel();
let source = TaskSource::new(
priority,
FutureWrapper::NonSend(ThreadGuard::new(f)),
Some(tx),
);
JoinHandle::new(self, source, rx)
}
// rustdoc-stripper-ignore-next
/// Spawn a new infallible `Future` on the main context from inside the main context.
///
/// The given `Future` does not have to be `Send` but the closure to spawn it has to be.
///
/// This can be called only from any thread.
pub fn spawn_from_within<R: 'static, F: Future<Output = R> + 'static>(
&self,
func: impl FnOnce() -> F + Send + 'static,
) -> SpawnWithinJoinHandle<R> {
self.spawn_from_within_with_priority(crate::Priority::default(), func)
}
// rustdoc-stripper-ignore-next
/// Spawn a new infallible `Future` on the main context from inside the main context.
///
/// The given `Future` does not have to be `Send` but the closure to spawn it has to be.
///
/// This can be called only from any thread.
pub fn spawn_from_within_with_priority<R: 'static, F: Future<Output = R> + 'static>(
&self,
priority: Priority,
func: impl FnOnce() -> F + Send + 'static,
) -> SpawnWithinJoinHandle<R> {
let ctx = self.clone();
let (tx, rx) = oneshot::channel();
self.invoke_with_priority(priority, move || {
let _ = tx.send(ctx.spawn_local(func()));
});
SpawnWithinJoinHandle {
rx: Some(rx),
join_handle: None,
}
}
// rustdoc-stripper-ignore-next
/// Runs a new, infallible `Future` on the main context and block until it finished, returning
/// the result of the `Future`.
///
/// The given `Future` does not have to be `Send` or `'static`.
///
/// This must only be called if no `MainLoop` or anything else is running on this specific main
/// context.
#[allow(clippy::transmute_ptr_to_ptr)]
pub fn block_on<F: Future>(&self, f: F) -> F::Output {
let mut res = None;
let l = MainLoop::new(Some(self), false);
let f = async {
res = Some(panic::AssertUnwindSafe(f).catch_unwind().await);
l.quit();
};
let f = unsafe {
// Super-unsafe: We transmute here to get rid of the 'static lifetime
let f = LocalFutureObj::new(Box::new(async move {
f.await;
Box::new(()) as Box<dyn Any + 'static>
}));
let f: LocalFutureObj<'static, Box<dyn Any + 'static>> = mem::transmute(f);
f
};
let source = TaskSource::new(
crate::Priority::default(),
FutureWrapper::NonSend(ThreadGuard::new(f)),
None,
);
source.attach(Some(self));
l.run();
match res.unwrap() {
Ok(v) => v,
Err(e) => panic::resume_unwind(e),
}
}
}
impl Spawn for MainContext {
fn spawn_obj(&self, f: FutureObj<'static, ()>) -> Result<(), SpawnError> {
let (tx, _) = oneshot::channel();
let source = TaskSource::new(
crate::Priority::default(),
FutureWrapper::Send(FutureObj::new(Box::new(async move {
f.await;
Box::new(()) as Box<dyn Any + Send + 'static>
}))),
Some(tx),
);
source.attach(Some(self));
Ok(())
}
}
impl LocalSpawn for MainContext {
fn spawn_local_obj(&self, f: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> {
let (tx, _) = oneshot::channel();
let source = TaskSource::new(
crate::Priority::default(),
FutureWrapper::NonSend(ThreadGuard::new(LocalFutureObj::new(Box::new(
async move {
f.await;
Box::new(()) as Box<dyn Any + 'static>
},
)))),
Some(tx),
);
source.attach(Some(self));
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::{sync::mpsc, thread};
use futures_channel::oneshot;
use futures_util::future::{FutureExt, TryFutureExt};
use super::*;
#[test]
fn test_spawn() {
let c = MainContext::new();
let l = crate::MainLoop::new(Some(&c), false);
let (sender, receiver) = mpsc::channel();
let (o_sender, o_receiver) = oneshot::channel();
let l_clone = l.clone();
c.spawn(
o_receiver
.and_then(move |()| {
sender.send(()).unwrap();
l_clone.quit();
futures_util::future::ok(())
})
.then(|res| {
assert!(res.is_ok());
futures_util::future::ready(())
}),
);
thread::spawn(move || {
l.run();
});
o_sender.send(()).unwrap();
receiver.recv().unwrap();
}
#[test]
fn test_spawn_local() {
let c = MainContext::new();
let l = crate::MainLoop::new(Some(&c), false);
c.with_thread_default(|| {
let l_clone = l.clone();
c.spawn_local(futures_util::future::lazy(move |_ctx| {
l_clone.quit();
}));
l.run();
})
.unwrap();
}
#[test]
fn test_spawn_from_within() {
let c = MainContext::new();
let l = crate::MainLoop::new(Some(&c), false);
std::thread::spawn({
let l_clone = l.clone();
move || {
c.spawn_from_within(move || async move {
l_clone.quit();
});
}
});
l.run();
}
#[test]
fn test_block_on() {
let c = MainContext::new();
let mut v = None;
{
let v = &mut v;
let future = futures_util::future::lazy(|_ctx| {
*v = Some(123);
Ok::<i32, ()>(123)
});
let res = c.block_on(future);
assert_eq!(res, Ok(123));
}
assert_eq!(v, Some(123));
}
#[test]
fn test_spawn_return() {
let c = MainContext::new();
c.block_on(async {
let val = 1;
let ret = c
.spawn(async move { futures_util::future::ready(2).await + val })
.await;
assert_eq!(ret.unwrap(), 3);
});
}
#[test]
fn test_spawn_panic() {
let c = MainContext::new();
c.block_on(async {
let ret = c
.spawn(async {
panic!("failed");
})
.await;
assert_eq!(
*ret.unwrap_err().into_panic().downcast::<&str>().unwrap(),
"failed"
);
});
}
}
@@ -0,0 +1,288 @@
// Take a look at the license at the top of the repository in the LICENSE file.
#[cfg(not(windows))]
use std::boxed::Box as Box_;
#[cfg(not(windows))]
use std::mem;
#[cfg(not(windows))]
#[cfg(feature = "v2_58")]
use std::os::unix::io::AsRawFd;
#[cfg(not(windows))]
use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd};
use std::ptr;
// #[cfg(windows)]
// #[cfg(feature = "v2_58")]
// use std::os::windows::io::AsRawHandle;
use crate::{translate::*, GStr};
#[cfg(not(windows))]
use crate::{Error, Pid, SpawnFlags};
#[cfg(feature = "v2_58")]
#[cfg(not(windows))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "v2_58", not(windows)))))]
#[allow(clippy::too_many_arguments)]
#[doc(alias = "g_spawn_async_with_fds")]
pub fn spawn_async_with_fds<P: AsRef<std::path::Path>, T: AsRawFd, U: AsRawFd, V: AsRawFd>(
working_directory: P,
argv: &[&str],
envp: &[&str],
flags: SpawnFlags,
child_setup: Option<Box_<dyn FnOnce() + 'static>>,
stdin_fd: T,
stdout_fd: U,
stderr_fd: V,
) -> Result<Pid, Error> {
let child_setup_data: Box_<Option<Box_<dyn FnOnce() + 'static>>> = Box_::new(child_setup);
unsafe extern "C" fn child_setup_func(user_data: ffi::gpointer) {
let callback: Box_<Option<Box_<dyn FnOnce() + 'static>>> =
Box_::from_raw(user_data as *mut _);
let callback = (*callback).expect("cannot get closure...");
callback()
}
let child_setup = if child_setup_data.is_some() {
Some(child_setup_func as _)
} else {
None
};
let super_callback0: Box_<Option<Box_<dyn FnOnce() + 'static>>> = child_setup_data;
unsafe {
let mut child_pid = mem::MaybeUninit::uninit();
let mut error = ptr::null_mut();
let _ = ffi::g_spawn_async_with_fds(
working_directory.as_ref().to_glib_none().0,
argv.to_glib_none().0,
envp.to_glib_none().0,
flags.into_glib(),
child_setup,
Box_::into_raw(super_callback0) as *mut _,
child_pid.as_mut_ptr(),
stdin_fd.as_raw_fd(),
stdout_fd.as_raw_fd(),
stderr_fd.as_raw_fd(),
&mut error,
);
let child_pid = from_glib(child_pid.assume_init());
if error.is_null() {
Ok(child_pid)
} else {
Err(from_glib_full(error))
}
}
}
// #[cfg(feature = "v2_58")]
// #[cfg(windows)]
// pub fn spawn_async_with_fds<
// P: AsRef<std::path::Path>,
// T: AsRawHandle,
// U: AsRawHandle,
// V: AsRawHandle,
// >(
// working_directory: P,
// argv: &[&str],
// envp: &[&str],
// flags: SpawnFlags,
// child_setup: Option<Box_<dyn FnOnce() + 'static>>,
// stdin_fd: T,
// stdout_fd: U,
// stderr_fd: V,
// ) -> Result<Pid, Error> {
// let child_setup_data: Box_<Option<Box_<dyn FnOnce() + 'static>>> = Box_::new(child_setup);
// unsafe extern "C" fn child_setup_func<P: AsRef<std::path::Path>>(
// user_data: ffi::gpointer,
// ) {
// let callback: Box_<Option<Box_<dyn FnOnce() + 'static>>> =
// Box_::from_raw(user_data as *mut _);
// let callback = (*callback).expect("cannot get closure...");
// callback()
// }
// let child_setup = if child_setup_data.is_some() {
// Some(child_setup_func::<P> as _)
// } else {
// None
// };
// let super_callback0: Box_<Option<Box_<dyn FnOnce() + 'static>>> = child_setup_data;
// unsafe {
// let mut child_pid = mem::MaybeUninit::uninit();
// let mut error = ptr::null_mut();
// let _ = ffi::g_spawn_async_with_fds(
// working_directory.as_ref().to_glib_none().0,
// argv.to_glib_none().0,
// envp.to_glib_none().0,
// flags.into_glib(),
// child_setup,
// Box_::into_raw(super_callback0) as *mut _,
// child_pid.as_mut_ptr(),
// stdin_fd.as_raw_handle() as usize as _,
// stdout_fd.as_raw_handle() as usize as _,
// stderr_fd.as_raw_handle() as usize as _,
// &mut error,
// );
// let child_pid = from_glib(child_pid.assume_init());
// if error.is_null() {
// Ok(child_pid)
// } else {
// Err(from_glib_full(error))
// }
// }
// }
#[cfg(not(windows))]
#[cfg_attr(docsrs, doc(cfg(not(windows))))]
#[doc(alias = "g_spawn_async_with_pipes")]
pub fn spawn_async_with_pipes<
P: AsRef<std::path::Path>,
T: FromRawFd,
U: FromRawFd,
V: FromRawFd,
>(
working_directory: P,
argv: &[&std::path::Path],
envp: &[&std::path::Path],
flags: SpawnFlags,
child_setup: Option<Box_<dyn FnOnce() + 'static>>,
) -> Result<(Pid, T, U, V), Error> {
let child_setup_data: Box_<Option<Box_<dyn FnOnce() + 'static>>> = Box_::new(child_setup);
unsafe extern "C" fn child_setup_func(user_data: ffi::gpointer) {
let callback: Box_<Option<Box_<dyn FnOnce() + 'static>>> =
Box_::from_raw(user_data as *mut _);
let callback = (*callback).expect("cannot get closure...");
callback()
}
let child_setup = if child_setup_data.is_some() {
Some(child_setup_func as _)
} else {
None
};
let super_callback0: Box_<Option<Box_<dyn FnOnce() + 'static>>> = child_setup_data;
unsafe {
let mut child_pid = mem::MaybeUninit::uninit();
let mut standard_input = mem::MaybeUninit::uninit();
let mut standard_output = mem::MaybeUninit::uninit();
let mut standard_error = mem::MaybeUninit::uninit();
let mut error = ptr::null_mut();
let _ = ffi::g_spawn_async_with_pipes(
working_directory.as_ref().to_glib_none().0,
argv.to_glib_none().0,
envp.to_glib_none().0,
flags.into_glib(),
child_setup,
Box_::into_raw(super_callback0) as *mut _,
child_pid.as_mut_ptr(),
standard_input.as_mut_ptr(),
standard_output.as_mut_ptr(),
standard_error.as_mut_ptr(),
&mut error,
);
let child_pid = from_glib(child_pid.assume_init());
let standard_input = standard_input.assume_init();
let standard_output = standard_output.assume_init();
let standard_error = standard_error.assume_init();
if error.is_null() {
#[cfg(not(windows))]
{
Ok((
child_pid,
FromRawFd::from_raw_fd(standard_input),
FromRawFd::from_raw_fd(standard_output),
FromRawFd::from_raw_fd(standard_error),
))
}
// #[cfg(windows)]
// {
// use std::os::windows::io::{FromRawHandle, RawHandle};
// Ok((
// child_pid,
// File::from_raw_handle(standard_input as usize as RawHandle),
// File::from_raw_handle(standard_output as usize as RawHandle),
// File::from_raw_handle(standard_error as usize as RawHandle),
// ))
// }
} else {
Err(from_glib_full(error))
}
}
}
// rustdoc-stripper-ignore-next
/// Obtain the character set for the current locale.
///
/// This returns whether the locale's encoding is UTF-8, and the current
/// charset if available.
#[doc(alias = "g_get_charset")]
#[doc(alias = "get_charset")]
pub fn charset() -> (bool, Option<&'static GStr>) {
unsafe {
let mut out_charset = ptr::null();
let is_utf8 = from_glib(ffi::g_get_charset(&mut out_charset));
let charset = from_glib_none(out_charset);
(is_utf8, charset)
}
}
#[cfg(unix)]
#[doc(alias = "g_unix_open_pipe")]
pub fn unix_open_pipe(flags: i32) -> Result<(RawFd, RawFd), Error> {
unsafe {
let mut fds = [0, 2];
let mut error = ptr::null_mut();
let _ = ffi::g_unix_open_pipe(&mut fds, flags, &mut error);
if error.is_null() {
Ok((
FromRawFd::from_raw_fd(fds[0]),
FromRawFd::from_raw_fd(fds[1]),
))
} else {
Err(from_glib_full(error))
}
}
}
#[cfg(unix)]
#[doc(alias = "g_file_open_tmp")]
pub fn file_open_tmp(
tmpl: Option<impl AsRef<std::path::Path>>,
) -> Result<(RawFd, std::path::PathBuf), crate::Error> {
unsafe {
let mut name_used = ptr::null_mut();
let mut error = ptr::null_mut();
let ret = ffi::g_file_open_tmp(
tmpl.as_ref().map(|p| p.as_ref()).to_glib_none().0,
&mut name_used,
&mut error,
);
if error.is_null() {
Ok((ret.into_raw_fd(), from_glib_full(name_used)))
} else {
Err(from_glib_full(error))
}
}
}
// rustdoc-stripper-ignore-next
/// Spawn a new infallible `Future` on the thread-default main context.
///
/// This can be called from any thread and will execute the future from the thread
/// where main context is running, e.g. via a `MainLoop`.
pub fn spawn_future<R: Send + 'static, F: std::future::Future<Output = R> + Send + 'static>(
f: F,
) -> crate::JoinHandle<R> {
let ctx = crate::MainContext::ref_thread_default();
ctx.spawn(f)
}
// rustdoc-stripper-ignore-next
/// Spawn a new infallible `Future` on the thread-default main context.
///
/// The given `Future` does not have to be `Send`.
///
/// This can be called only from the thread where the main context is running, e.g.
/// from any other `Future` that is executed on this main context, or after calling
/// `with_thread_default` or `acquire` on the main context.
pub fn spawn_future_local<R: 'static, F: std::future::Future<Output = R> + 'static>(
f: F,
) -> crate::JoinHandle<R> {
let ctx = crate::MainContext::ref_thread_default();
ctx.spawn_local(f)
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+9
View File
@@ -0,0 +1,9 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! Traits and essential types intended for blanket imports.
pub use crate::{
param_spec::ParamSpecBuilderExt, Cast, CastNone, IsA, ObjectExt, ObjectType, ParamSpecType,
StaticType, StaticTypeExt, StaticVariantType, ToSendValue, ToValue, ToVariant,
};
+303
View File
@@ -0,0 +1,303 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::cell::Cell;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::RwLock;
use crate::HasParamSpec;
use crate::IsA;
use crate::Object;
use crate::SendWeakRef;
use crate::WeakRef;
// rustdoc-stripper-ignore-next
/// A type that can be used as a property. It covers every type which have an associated `ParamSpec`
/// (`HasParamSpec`) and some useful types wrapping `HasParamSpec`.
/// The definition is recursive, so you can nest many `Property`s together. The final `ParamSpec` will
/// be the one of the innermost type
pub trait Property {
type Value: HasParamSpec;
}
impl<T: HasParamSpec> Property for T {
type Value = T;
}
impl<T: Property> Property for PhantomData<T> {
type Value = T::Value;
}
impl<T: Property> Property for RefCell<T> {
type Value = T::Value;
}
impl<T: Property> Property for Cell<T> {
type Value = T::Value;
}
impl<T: Property> Property for Mutex<T> {
type Value = T::Value;
}
impl<T: Property> Property for RwLock<T> {
type Value = T::Value;
}
impl<T: Property> Property for once_cell::sync::OnceCell<T> {
type Value = T::Value;
}
impl<T: Property> Property for once_cell::unsync::OnceCell<T> {
type Value = T::Value;
}
impl<T: Property> Property for std::cell::OnceCell<T> {
type Value = T::Value;
}
impl<T: Property> Property for std::sync::OnceLock<T> {
type Value = T::Value;
}
// Handle smart pointers trasparently
impl<T: Property> Property for Rc<T> {
type Value = T::Value;
}
impl<T: Property> Property for Arc<T> {
type Value = T::Value;
}
impl<T: IsA<Object> + HasParamSpec> Property for WeakRef<T> {
type Value = Option<T>;
}
impl<T: IsA<Object> + HasParamSpec> Property for SendWeakRef<T> {
type Value = Option<T>;
}
// rustdoc-stripper-ignore-next
/// A container type implementing this trait can be read by the default getter generated by the `Props` macro.
pub trait PropertyGet {
type Value;
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R;
}
// rustdoc-stripper-ignore-next
/// A container type implementing this trait can be written by the default setter generated by the `Props` macro.
/// It takes a `FnOnce(&mut Self::Value)` so that the caller may access nested fields of a struct
/// by doing `${Self::Value}.member`
pub trait PropertySetNested {
type SetNestedValue;
fn set_nested<F: FnOnce(&mut Self::SetNestedValue)>(&self, f: F);
}
// rustdoc-stripper-ignore-next
/// A container type implementing this trait can be written by the default setter generated by the `Props` macro.
pub trait PropertySet {
type SetValue;
fn set(&self, v: Self::SetValue);
}
impl<T: PropertySetNested> PropertySet for T {
type SetValue = T::SetNestedValue;
fn set(&self, v: Self::SetValue) {
self.set_nested(|x| *x = v);
}
}
impl<T: HasParamSpec> PropertyGet for T {
type Value = T;
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
f(self)
}
}
impl<T: Copy> PropertyGet for Cell<T> {
type Value = T;
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
f(&Cell::get(self))
}
}
impl<T> PropertySet for Cell<T> {
type SetValue = T;
fn set(&self, v: Self::SetValue) {
self.set(v);
}
}
impl<T> PropertyGet for RefCell<T> {
type Value = T;
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
f(&self.borrow())
}
}
impl<T> PropertySetNested for RefCell<T> {
type SetNestedValue = T;
fn set_nested<F: FnOnce(&mut Self::SetNestedValue)>(&self, f: F) {
f(&mut self.borrow_mut());
}
}
impl<T> PropertyGet for Mutex<T> {
type Value = T;
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
f(&self.lock().unwrap())
}
}
impl<T> PropertySetNested for Mutex<T> {
type SetNestedValue = T;
fn set_nested<F: FnOnce(&mut Self::SetNestedValue)>(&self, f: F) {
f(&mut self.lock().unwrap());
}
}
impl<T> PropertyGet for RwLock<T> {
type Value = T;
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
f(&self.read().unwrap())
}
}
impl<T> PropertySetNested for RwLock<T> {
type SetNestedValue = T;
fn set_nested<F: FnOnce(&mut Self::SetNestedValue)>(&self, f: F) {
f(&mut self.write().unwrap());
}
}
impl<T> PropertyGet for once_cell::sync::OnceCell<T> {
type Value = T;
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
f(self.get().unwrap())
}
}
impl<T> PropertyGet for once_cell::unsync::OnceCell<T> {
type Value = T;
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
f(self.get().unwrap())
}
}
impl<T> PropertySet for once_cell::sync::OnceCell<T> {
type SetValue = T;
fn set(&self, v: Self::SetValue) {
// I can't use `unwrap` because I would have to add a `Debug` bound to _v
if let Err(_v) = self.set(v) {
panic!("can't set value of OnceCell multiple times")
};
}
}
impl<T> PropertySet for once_cell::unsync::OnceCell<T> {
type SetValue = T;
fn set(&self, v: Self::SetValue) {
// I can't use `unwrap` because I would have to add a `Debug` bound to _v
if let Err(_v) = self.set(v) {
panic!("can't set value of OnceCell multiple times")
};
}
}
impl<T> PropertyGet for std::cell::OnceCell<T> {
type Value = T;
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
f(self.get().unwrap())
}
}
impl<T> PropertyGet for std::sync::OnceLock<T> {
type Value = T;
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
f(self.get().unwrap())
}
}
impl<T> PropertySet for std::cell::OnceCell<T> {
type SetValue = T;
fn set(&self, v: Self::SetValue) {
// I can't use `unwrap` because I would have to add a `Debug` bound to _v
if let Err(_v) = self.set(v) {
panic!("can't set value of OnceCell multiple times")
};
}
}
impl<T> PropertySet for std::sync::OnceLock<T> {
type SetValue = T;
fn set(&self, v: Self::SetValue) {
// I can't use `unwrap` because I would have to add a `Debug` bound to _v
if let Err(_v) = self.set(v) {
panic!("can't set value of OnceCell multiple times")
};
}
}
impl<T: IsA<Object>> PropertyGet for WeakRef<T> {
type Value = Option<T>;
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
f(&self.upgrade())
}
}
impl<T: IsA<Object>> PropertySet for WeakRef<T> {
type SetValue = Option<T>;
fn set(&self, v: Self::SetValue) {
self.set(v.as_ref())
}
}
impl<T: IsA<Object>> PropertyGet for SendWeakRef<T> {
type Value = Option<T>;
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
f(&self.upgrade())
}
}
impl<T: IsA<Object>> PropertySet for SendWeakRef<T> {
type SetValue = Option<T>;
fn set(&self, v: Self::SetValue) {
WeakRef::set(self, v.as_ref());
}
}
// Smart pointers wrapping a `PropertyRead`/`PropertyWrite`
impl<T: PropertyGet> PropertyGet for Rc<T> {
type Value = T::Value;
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
(**self).get(f)
}
}
impl<T: PropertySetNested> PropertySetNested for Rc<T> {
type SetNestedValue = T::SetNestedValue;
fn set_nested<F: FnOnce(&mut Self::SetNestedValue)>(&self, f: F) {
(**self).set_nested(f)
}
}
impl<T: PropertyGet> PropertyGet for Arc<T> {
type Value = T::Value;
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
(**self).get(f)
}
}
impl<T: PropertySetNested> PropertySetNested for Arc<T> {
type SetNestedValue = T::SetNestedValue;
fn set_nested<F: FnOnce(&mut Self::SetNestedValue)>(&self, f: F) {
(**self).set_nested(f)
}
}
macro_rules! impl_atomic {
($atomic:ty, $valuety:ty) => {
impl Property for $atomic {
type Value = $valuety;
}
impl PropertyGet for $atomic {
type Value = $valuety;
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
f(&self.load(Ordering::Acquire))
}
}
impl PropertySet for $atomic {
type SetValue = $valuety;
fn set(&self, v: Self::SetValue) {
self.store(v, Ordering::Release);
}
}
};
}
impl_atomic!(std::sync::atomic::AtomicBool, bool);
impl_atomic!(std::sync::atomic::AtomicI8, i8);
impl_atomic!(std::sync::atomic::AtomicI32, i32);
#[cfg(target_has_atomic = "64")]
impl_atomic!(std::sync::atomic::AtomicI64, i64);
impl_atomic!(std::sync::atomic::AtomicU8, u8);
impl_atomic!(std::sync::atomic::AtomicU32, u32);
#[cfg(target_has_atomic = "64")]
impl_atomic!(std::sync::atomic::AtomicU64, u64);
+103
View File
@@ -0,0 +1,103 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{fmt, num::NonZeroU32};
use crate::{translate::*, GStr, IntoGStr};
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
#[repr(transparent)]
#[doc(alias = "GQuark")]
pub struct Quark(NonZeroU32);
impl Quark {
#[doc(alias = "g_quark_from_string")]
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: impl IntoGStr) -> Quark {
unsafe { s.run_with_gstr(|s| from_glib(ffi::g_quark_from_string(s.as_ptr()))) }
}
#[allow(clippy::trivially_copy_pass_by_ref)]
#[doc(alias = "g_quark_to_string")]
pub fn as_str<'a>(&self) -> &'a GStr {
unsafe { GStr::from_ptr(ffi::g_quark_to_string(self.into_glib())) }
}
#[doc(alias = "g_quark_try_string")]
pub fn try_from_str(s: &str) -> Option<Quark> {
unsafe { Self::try_from_glib(ffi::g_quark_try_string(s.to_glib_none().0)).ok() }
}
}
impl fmt::Debug for Quark {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.write_str(Quark::as_str(self))
}
}
impl<T: IntoGStr> From<T> for Quark {
fn from(s: T) -> Self {
Self::from_str(s)
}
}
impl std::str::FromStr for Quark {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::from_str(s))
}
}
#[doc(hidden)]
impl FromGlib<ffi::GQuark> for Quark {
#[inline]
unsafe fn from_glib(value: ffi::GQuark) -> Self {
debug_assert_ne!(value, 0);
Self(NonZeroU32::new_unchecked(value))
}
}
#[doc(hidden)]
impl TryFromGlib<ffi::GQuark> for Quark {
type Error = GlibNoneError;
unsafe fn try_from_glib(value: ffi::GQuark) -> Result<Self, Self::Error> {
if value == 0 {
Err(GlibNoneError)
} else {
Ok(Self(NonZeroU32::new_unchecked(value)))
}
}
}
#[doc(hidden)]
impl IntoGlib for Quark {
type GlibType = ffi::GQuark;
#[inline]
fn into_glib(self) -> ffi::GQuark {
self.0.get()
}
}
#[doc(hidden)]
impl IntoGlib for Option<Quark> {
type GlibType = ffi::GQuark;
#[inline]
fn into_glib(self) -> ffi::GQuark {
self.map(|s| s.0.get()).unwrap_or(0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_str() {
let q1 = Quark::from_str("some-quark");
let q2 = Quark::try_from_str("some-quark");
assert_eq!(Some(q1), q2);
assert_eq!(q1.as_str(), "some-quark");
}
}
+588
View File
@@ -0,0 +1,588 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! `IMPL` Shared (reference counted) wrapper implementation.
use std::{
cmp, fmt,
hash::{Hash, Hasher},
marker::PhantomData,
ptr,
};
use crate::translate::*;
// rustdoc-stripper-ignore-next
/// Wrapper implementations for shared types. See `wrapper!`.
#[macro_export]
macro_rules! glib_shared_wrapper {
([$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty,
@ref $ref_arg:ident $ref_expr:expr, @unref $unref_arg:ident $unref_expr:expr
$(, @type_ $get_type_expr:expr)?) => {
$crate::glib_shared_wrapper!(
@generic_impl [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name,
@ref $ref_arg $ref_expr, @unref $unref_arg $unref_expr
);
$crate::glib_shared_wrapper!(@value_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name $(, @type_ $get_type_expr)?);
};
(@generic_impl [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty,
@ref $ref_arg:ident $ref_expr:expr, @unref $unref_arg:ident $unref_expr:expr) => {
$(#[$attr])*
#[repr(transparent)]
$visibility struct $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? {
inner: $crate::shared::Shared<$ffi_name, Self>,
}
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $name $(<$($generic),+>)? {
#[doc = "Return the inner pointer to the underlying C value."]
#[inline]
pub fn as_ptr(&self) -> *mut $ffi_name {
unsafe { *(self as *const Self as *const *const $ffi_name) as *mut $ffi_name }
}
#[doc = "Borrows the underlying C value."]
#[inline]
pub unsafe fn from_glib_ptr_borrow<'a>(ptr: *const *const $ffi_name) -> &'a Self {
&*(ptr as *const Self)
}
}
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::clone::Clone for $name $(<$($generic),+>)? {
#[inline]
fn clone(&self) -> Self {
Self {
inner: std::clone::Clone::clone(&self.inner),
}
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::shared::SharedMemoryManager<$ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn ref_($ref_arg: *mut $ffi_name) {
$ref_expr;
}
#[inline]
#[allow(clippy::no_effect)]
unsafe fn unref($unref_arg: *mut $ffi_name) {
$unref_expr;
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::GlibPtrDefault for $name $(<$($generic),+>)? {
type GlibType = *mut $ffi_name;
}
#[doc(hidden)]
unsafe impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::TransparentPtrType for $name $(<$($generic),+>)? {}
#[doc(hidden)]
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibPtr<'a, *mut $ffi_name> for $name $(<$($generic),+>)? {
type Storage = std::marker::PhantomData<&'a $crate::shared::Shared<$ffi_name, Self>>;
#[inline]
fn to_glib_none(&'a self) -> $crate::translate::Stash<'a, *mut $ffi_name, Self> {
let stash = $crate::translate::ToGlibPtr::to_glib_none(&self.inner);
$crate::translate::Stash(stash.0, stash.1)
}
#[inline]
fn to_glib_full(&self) -> *mut $ffi_name {
$crate::translate::ToGlibPtr::to_glib_full(&self.inner)
}
}
#[doc(hidden)]
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibPtr<'a, *const $ffi_name> for $name $(<$($generic),+>)? {
type Storage = std::marker::PhantomData<&'a $crate::shared::Shared<$ffi_name, Self>>;
#[inline]
fn to_glib_none(&'a self) -> $crate::translate::Stash<'a, *const $ffi_name, Self> {
let stash = $crate::translate::ToGlibPtr::to_glib_none(&self.inner);
$crate::translate::Stash(stash.0 as *const _, stash.1)
}
#[inline]
fn to_glib_full(&self) -> *const $ffi_name {
$crate::translate::ToGlibPtr::to_glib_full(&self.inner) as *const _
}
}
#[doc(hidden)]
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *mut *mut $ffi_name> for $name $(<$($generic),+>)? {
type Storage = (std::marker::PhantomData<&'a [Self]>, Option<Vec<*mut $ffi_name>>);
fn to_glib_none_from_slice(t: &'a [Self]) -> (*mut *mut $ffi_name, Self::Storage) {
let mut v_ptr = Vec::with_capacity(t.len() + 1);
unsafe {
let ptr = v_ptr.as_mut_ptr();
std::ptr::copy_nonoverlapping(t.as_ptr() as *mut *mut $ffi_name, ptr, t.len());
std::ptr::write(ptr.add(t.len()), std::ptr::null_mut());
v_ptr.set_len(t.len() + 1);
}
(v_ptr.as_ptr() as *mut *mut $ffi_name, (std::marker::PhantomData, Some(v_ptr)))
}
fn to_glib_container_from_slice(t: &'a [Self]) -> (*mut *mut $ffi_name, Self::Storage) {
let v_ptr = unsafe {
let v_ptr = $crate::ffi::g_malloc(std::mem::size_of::<*mut $ffi_name>() * (t.len() + 1)) as *mut *mut $ffi_name;
std::ptr::copy_nonoverlapping(t.as_ptr() as *mut *mut $ffi_name, v_ptr, t.len());
std::ptr::write(v_ptr.add(t.len()), std::ptr::null_mut());
v_ptr
};
(v_ptr, (std::marker::PhantomData, None))
}
fn to_glib_full_from_slice(t: &[Self]) -> *mut *mut $ffi_name {
unsafe {
let v_ptr = $crate::ffi::g_malloc(std::mem::size_of::<*mut $ffi_name>() * (t.len() + 1)) as *mut *mut $ffi_name;
for (i, s) in t.iter().enumerate() {
std::ptr::write(v_ptr.add(i), $crate::translate::ToGlibPtr::to_glib_full(s));
}
std::ptr::write(v_ptr.add(t.len()), std::ptr::null_mut());
v_ptr
}
}
}
#[doc(hidden)]
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *const *mut $ffi_name> for $name $(<$($generic),+>)? {
type Storage = (std::marker::PhantomData<&'a [Self]>, Option<Vec<*mut $ffi_name>>);
fn to_glib_none_from_slice(t: &'a [Self]) -> (*const *mut $ffi_name, Self::Storage) {
let (ptr, stash) = $crate::translate::ToGlibContainerFromSlice::<'a, *mut *mut $ffi_name>::to_glib_none_from_slice(t);
(ptr as *const *mut $ffi_name, stash)
}
fn to_glib_container_from_slice(_: &'a [Self]) -> (*const *mut $ffi_name, Self::Storage) {
// Can't have consumer free a *const pointer
unimplemented!()
}
fn to_glib_full_from_slice(_: &[Self]) -> *const *mut $ffi_name {
// Can't have consumer free a *const pointer
unimplemented!()
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrNone<*mut $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_none(ptr: *mut $ffi_name) -> Self {
Self {
inner: $crate::translate::from_glib_none(ptr),
}
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrNone<*const $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_none(ptr: *const $ffi_name) -> Self {
Self {
inner: $crate::translate::from_glib_none(ptr),
}
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrFull<*mut $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_full(ptr: *mut $ffi_name) -> Self {
Self {
inner: $crate::translate::from_glib_full(ptr),
}
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrBorrow<*mut $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_borrow(ptr: *mut $ffi_name) -> $crate::translate::Borrowed<Self> {
$crate::translate::Borrowed::new(
Self {
inner: $crate::translate::from_glib_borrow::<_, $crate::shared::Shared<_, _>>(ptr).into_inner(),
}
)
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrBorrow<*const $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn from_glib_borrow(ptr: *const $ffi_name) -> $crate::translate::Borrowed<Self> {
$crate::translate::from_glib_borrow::<_, Self>(ptr as *mut $ffi_name)
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibContainerAsVec<*mut $ffi_name, *mut *mut $ffi_name> for $name $(<$($generic),+>)? {
unsafe fn from_glib_none_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
if num == 0 || ptr.is_null() {
return Vec::new();
}
let mut res = Vec::<Self>::with_capacity(num);
let res_ptr = res.as_mut_ptr();
for i in 0..num {
::std::ptr::write(res_ptr.add(i), $crate::translate::from_glib_none(std::ptr::read(ptr.add(i))));
}
res.set_len(num);
res
}
unsafe fn from_glib_container_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
let res = $crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, num);
$crate::ffi::g_free(ptr as *mut _);
res
}
unsafe fn from_glib_full_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
if num == 0 || ptr.is_null() {
$crate::ffi::g_free(ptr as *mut _);
return Vec::new();
}
let mut res = Vec::with_capacity(num);
let res_ptr = res.as_mut_ptr();
::std::ptr::copy_nonoverlapping(ptr as *mut Self, res_ptr, num);
res.set_len(num);
$crate::ffi::g_free(ptr as *mut _);
res
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrArrayContainerAsVec<*mut $ffi_name, *mut *mut $ffi_name> for $name $(<$($generic),+>)? {
unsafe fn from_glib_none_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
$crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
}
unsafe fn from_glib_container_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
$crate::translate::FromGlibContainerAsVec::from_glib_container_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
}
unsafe fn from_glib_full_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
$crate::translate::FromGlibContainerAsVec::from_glib_full_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibContainerAsVec<*mut $ffi_name, *const *mut $ffi_name> for $name $(<$($generic),+>)? {
unsafe fn from_glib_none_num_as_vec(ptr: *const *mut $ffi_name, num: usize) -> Vec<Self> {
$crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr as *mut *mut _, num)
}
unsafe fn from_glib_container_num_as_vec(_: *const *mut $ffi_name, _: usize) -> Vec<Self> {
// Can't free a *const
unimplemented!()
}
unsafe fn from_glib_full_num_as_vec(_: *const *mut $ffi_name, _: usize) -> Vec<Self> {
// Can't free a *const
unimplemented!()
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrArrayContainerAsVec<*mut $ffi_name, *const *mut $ffi_name> for $name $(<$($generic),+>)? {
unsafe fn from_glib_none_as_vec(ptr: *const *mut $ffi_name) -> Vec<Self> {
$crate::translate::FromGlibPtrArrayContainerAsVec::from_glib_none_as_vec(ptr as *mut *mut _)
}
unsafe fn from_glib_container_as_vec(_: *const *mut $ffi_name) -> Vec<Self> {
// Can't free a *const
unimplemented!()
}
unsafe fn from_glib_full_as_vec(_: *const *mut $ffi_name) -> Vec<Self> {
// Can't free a *const
unimplemented!()
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::IntoGlibPtr<*mut $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn into_glib_ptr(self) -> *mut $ffi_name {
let s = std::mem::ManuallyDrop::new(self);
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_none(&*s).0 as *mut _
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::IntoGlibPtr<*const $ffi_name> for $name $(<$($generic),+>)? {
#[inline]
unsafe fn into_glib_ptr(self) -> *const $ffi_name {
let s = std::mem::ManuallyDrop::new(self);
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_none(&*s).0 as *const _
}
}
};
(@value_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty) => { };
(@value_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty, @type_ $get_type_expr:expr) => {
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::types::StaticType for $name $(<$($generic),+>)? {
#[inline]
fn static_type() -> $crate::types::Type {
#[allow(unused_unsafe)]
unsafe { $crate::translate::from_glib($get_type_expr) }
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ValueType for $name $(<$($generic),+>)? {
type Type = Self;
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ValueTypeOptional for $name $(<$($generic),+>)? { }
#[doc(hidden)]
unsafe impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::value::FromValue<'a> for $name $(<$($generic),+>)? {
type Checker = $crate::value::GenericValueTypeOrNoneChecker<Self>;
#[inline]
unsafe fn from_value(value: &'a $crate::Value) -> Self {
let ptr = $crate::gobject_ffi::g_value_dup_boxed($crate::translate::ToGlibPtr::to_glib_none(value).0);
debug_assert!(!ptr.is_null());
<Self as $crate::translate::FromGlibPtrFull<*mut $ffi_name>>::from_glib_full(ptr as *mut $ffi_name)
}
}
#[doc(hidden)]
unsafe impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::value::FromValue<'a> for &'a $name $(<$($generic),+>)? {
type Checker = $crate::value::GenericValueTypeOrNoneChecker<Self>;
#[inline]
unsafe fn from_value(value: &'a $crate::Value) -> Self {
debug_assert_eq!(std::mem::size_of::<Self>(), std::mem::size_of::<$crate::ffi::gpointer>());
let value = &*(value as *const $crate::Value as *const $crate::gobject_ffi::GValue);
debug_assert!(!value.data[0].v_pointer.is_null());
<$name $(<$($generic),+>)?>::from_glib_ptr_borrow(&value.data[0].v_pointer as *const $crate::ffi::gpointer as *const *const $ffi_name)
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ToValue for $name $(<$($generic),+>)? {
#[inline]
fn to_value(&self) -> $crate::Value {
unsafe {
let mut value = $crate::Value::from_type_unchecked(<Self as $crate::StaticType>::static_type());
$crate::gobject_ffi::g_value_take_boxed(
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
$crate::translate::ToGlibPtr::<*mut $ffi_name>::to_glib_full(self) as *mut _,
);
value
}
}
#[inline]
fn value_type(&self) -> $crate::Type {
<Self as $crate::StaticType>::static_type()
}
}
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::convert::From<$name $(<$($generic),+>)?> for $crate::Value {
#[inline]
fn from(s: $name $(<$($generic),+>)?) -> Self {
unsafe {
let mut value = $crate::Value::from_type_unchecked(<$name $(<$($generic),+>)? as $crate::StaticType>::static_type());
$crate::gobject_ffi::g_value_take_boxed(
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
$crate::translate::IntoGlibPtr::<*mut $ffi_name>::into_glib_ptr(s) as *mut _,
);
value
}
}
}
#[doc(hidden)]
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ToValueOptional for $name $(<$($generic),+>)? {
#[inline]
fn to_value_optional(s: Option<&Self>) -> $crate::Value {
let mut value = $crate::Value::for_value_type::<Self>();
unsafe {
$crate::gobject_ffi::g_value_take_boxed(
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
$crate::translate::ToGlibPtr::<*mut $ffi_name>::to_glib_full(&s) as *mut _,
);
}
value
}
}
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::HasParamSpec for $name $(<$($generic),+>)? {
type ParamSpec = $crate::ParamSpecBoxed;
type SetValue = Self;
type BuilderFn = fn(&str) -> $crate::ParamSpecBoxedBuilder<Self>;
fn param_spec_builder() -> Self::BuilderFn {
|name| Self::ParamSpec::builder(name)
}
}
};
}
pub trait SharedMemoryManager<T> {
/// # Safety
///
/// Callers are responsible for ensuring that a matching call to `unref`
/// is made at an appropriate time.
unsafe fn ref_(ptr: *mut T);
/// # Safety
///
/// Callers are responsible for ensuring that a matching call to `ref` was
/// made before this is called, and that the pointer is not used after the
/// `unref` call.
unsafe fn unref(ptr: *mut T);
}
/// Encapsulates memory management logic for shared types.
#[repr(transparent)]
pub struct Shared<T, MM: SharedMemoryManager<T>> {
inner: ptr::NonNull<T>,
mm: PhantomData<*const MM>,
}
impl<T, MM: SharedMemoryManager<T>> Drop for Shared<T, MM> {
#[inline]
fn drop(&mut self) {
unsafe {
MM::unref(self.inner.as_ptr());
}
}
}
impl<T, MM: SharedMemoryManager<T>> Clone for Shared<T, MM> {
#[inline]
fn clone(&self) -> Self {
unsafe {
MM::ref_(self.inner.as_ptr());
}
Self {
inner: self.inner,
mm: PhantomData,
}
}
}
impl<T, MM: SharedMemoryManager<T>> fmt::Debug for Shared<T, MM> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Shared")
.field("inner", &self.inner)
.finish()
}
}
impl<T, MM: SharedMemoryManager<T>> PartialOrd for Shared<T, MM> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T, MM: SharedMemoryManager<T>> Ord for Shared<T, MM> {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.inner.cmp(&other.inner)
}
}
impl<T, MM: SharedMemoryManager<T>> PartialEq for Shared<T, MM> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
impl<T, MM: SharedMemoryManager<T>> Eq for Shared<T, MM> {}
impl<T, MM: SharedMemoryManager<T>> Hash for Shared<T, MM> {
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.inner.hash(state)
}
}
impl<'a, T: 'static, MM> ToGlibPtr<'a, *mut T> for Shared<T, MM>
where
MM: SharedMemoryManager<T> + 'static,
{
type Storage = PhantomData<&'a Self>;
#[inline]
fn to_glib_none(&'a self) -> Stash<'a, *mut T, Self> {
Stash(self.inner.as_ptr(), PhantomData)
}
#[inline]
fn to_glib_full(&self) -> *mut T {
unsafe {
MM::ref_(self.inner.as_ptr());
}
self.inner.as_ptr()
}
}
impl<T: 'static, MM: SharedMemoryManager<T>> FromGlibPtrNone<*mut T> for Shared<T, MM> {
#[inline]
unsafe fn from_glib_none(ptr: *mut T) -> Self {
debug_assert!(!ptr.is_null());
MM::ref_(ptr);
Self {
inner: ptr::NonNull::new_unchecked(ptr),
mm: PhantomData,
}
}
}
impl<T: 'static, MM: SharedMemoryManager<T>> FromGlibPtrNone<*const T> for Shared<T, MM> {
#[inline]
unsafe fn from_glib_none(ptr: *const T) -> Self {
debug_assert!(!ptr.is_null());
MM::ref_(ptr as *mut _);
Self {
inner: ptr::NonNull::new_unchecked(ptr as *mut _),
mm: PhantomData,
}
}
}
impl<T: 'static, MM: SharedMemoryManager<T>> FromGlibPtrFull<*mut T> for Shared<T, MM> {
#[inline]
unsafe fn from_glib_full(ptr: *mut T) -> Self {
debug_assert!(!ptr.is_null());
Self {
inner: ptr::NonNull::new_unchecked(ptr),
mm: PhantomData,
}
}
}
impl<T: 'static, MM: SharedMemoryManager<T>> FromGlibPtrBorrow<*mut T> for Shared<T, MM> {
#[inline]
unsafe fn from_glib_borrow(ptr: *mut T) -> Borrowed<Self> {
debug_assert!(!ptr.is_null());
Borrowed::new(Self {
inner: ptr::NonNull::new_unchecked(ptr),
mm: PhantomData,
})
}
}
+226
View File
@@ -0,0 +1,226 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! `IMPL` Low level signal support.
use std::{mem, num::NonZeroU64};
use ffi::gpointer;
use gobject_ffi::{self, GCallback};
use libc::{c_char, c_ulong, c_void};
use crate::{prelude::*, translate::*};
// rustdoc-stripper-ignore-next
/// The id of a signal that is returned by `connect`.
///
/// This type does not implement `Clone` to prevent disconnecting
/// the same signal handler multiple times.
///
/// ```ignore
/// use glib::SignalHandlerId;
/// use gtk::prelude::*;
/// use std::cell::RefCell;
///
/// struct Button {
/// widget: gtk::Button,
/// clicked_handler_id: RefCell<Option<SignalHandlerId>>,
/// }
///
/// impl Button {
/// fn new() -> Self {
/// let widget = gtk::Button::new();
/// let clicked_handler_id = RefCell::new(Some(widget.connect_clicked(|_button| {
/// // Do something.
/// })));
/// Self {
/// widget,
/// clicked_handler_id,
/// }
/// }
///
/// fn disconnect(&self) {
/// if let Some(id) = self.clicked_handler_id.take() {
/// self.widget.disconnect(id)
/// }
/// }
/// }
/// ```
#[derive(Debug, Eq, PartialEq)]
pub struct SignalHandlerId(NonZeroU64);
impl SignalHandlerId {
// rustdoc-stripper-ignore-next
/// Returns the internal signal handler ID.
pub unsafe fn as_raw(&self) -> libc::c_ulong {
self.0.get() as libc::c_ulong
}
}
impl FromGlib<c_ulong> for SignalHandlerId {
#[inline]
unsafe fn from_glib(val: c_ulong) -> Self {
debug_assert_ne!(val, 0);
Self(NonZeroU64::new_unchecked(val as _))
}
}
pub unsafe fn connect_raw<F>(
receiver: *mut gobject_ffi::GObject,
signal_name: *const c_char,
trampoline: GCallback,
closure: *mut F,
) -> SignalHandlerId {
unsafe extern "C" fn destroy_closure<F>(ptr: *mut c_void, _: *mut gobject_ffi::GClosure) {
// destroy
let _ = Box::<F>::from_raw(ptr as *mut _);
}
debug_assert_eq!(mem::size_of::<*mut F>(), mem::size_of::<gpointer>());
debug_assert!(trampoline.is_some());
let handle = gobject_ffi::g_signal_connect_data(
receiver,
signal_name,
trampoline,
closure as *mut _,
Some(destroy_closure::<F>),
0,
);
debug_assert!(handle > 0);
from_glib(handle)
}
#[doc(alias = "g_signal_handler_block")]
pub fn signal_handler_block<T: ObjectType>(instance: &T, handler_id: &SignalHandlerId) {
unsafe {
gobject_ffi::g_signal_handler_block(
instance.as_object_ref().to_glib_none().0,
handler_id.as_raw(),
);
}
}
#[doc(alias = "g_signal_handler_unblock")]
pub fn signal_handler_unblock<T: ObjectType>(instance: &T, handler_id: &SignalHandlerId) {
unsafe {
gobject_ffi::g_signal_handler_unblock(
instance.as_object_ref().to_glib_none().0,
handler_id.as_raw(),
);
}
}
#[allow(clippy::needless_pass_by_value)]
#[doc(alias = "g_signal_handler_disconnect")]
pub fn signal_handler_disconnect<T: ObjectType>(instance: &T, handler_id: SignalHandlerId) {
unsafe {
gobject_ffi::g_signal_handler_disconnect(
instance.as_object_ref().to_glib_none().0,
handler_id.as_raw(),
);
}
}
#[doc(alias = "g_signal_stop_emission_by_name")]
pub fn signal_stop_emission_by_name<T: ObjectType>(instance: &T, signal_name: &str) {
unsafe {
gobject_ffi::g_signal_stop_emission_by_name(
instance.as_object_ref().to_glib_none().0,
signal_name.to_glib_none().0,
);
}
}
#[doc(alias = "g_signal_has_handler_pending")]
pub fn signal_has_handler_pending<T: ObjectType>(
instance: &T,
signal_id: crate::subclass::SignalId,
detail: Option<crate::Quark>,
may_be_blocked: bool,
) -> bool {
unsafe {
from_glib(gobject_ffi::g_signal_has_handler_pending(
instance.as_object_ref().to_glib_none().0,
signal_id.into_glib(),
detail.map_or(0, |d| d.into_glib()),
may_be_blocked.into_glib(),
))
}
}
// rustdoc-stripper-ignore-next
/// Whether to invoke the other event handlers.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Propagation {
// Stop other handlers from being invoked for the event.
Stop,
// Propagate the event further.
Proceed,
}
impl Propagation {
// rustdoc-stripper-ignore-next
/// Returns `true` if this is a `Stop` variant.
pub fn is_stop(&self) -> bool {
matches!(self, Self::Stop)
}
// rustdoc-stripper-ignore-next
/// Returns `true` if this is a `Proceed` variant.
pub fn is_proceed(&self) -> bool {
matches!(self, Self::Proceed)
}
}
impl From<bool> for Propagation {
fn from(value: bool) -> Self {
if value {
Self::Stop
} else {
Self::Proceed
}
}
}
impl From<Propagation> for bool {
fn from(c: Propagation) -> Self {
match c {
Propagation::Stop => true,
Propagation::Proceed => false,
}
}
}
#[doc(hidden)]
impl IntoGlib for Propagation {
type GlibType = ffi::gboolean;
#[inline]
fn into_glib(self) -> ffi::gboolean {
bool::from(self).into_glib()
}
}
#[doc(hidden)]
impl FromGlib<ffi::gboolean> for Propagation {
#[inline]
unsafe fn from_glib(value: ffi::gboolean) -> Self {
bool::from_glib(value).into()
}
}
impl crate::ToValue for Propagation {
fn to_value(&self) -> crate::Value {
bool::from(*self).to_value()
}
fn value_type(&self) -> crate::Type {
<bool as crate::StaticType>::static_type()
}
}
impl From<Propagation> for crate::Value {
#[inline]
fn from(v: Propagation) -> Self {
bool::from(v).into()
}
}
File diff suppressed because it is too large. Load diff
+482
View File
@@ -0,0 +1,482 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{marker::Unpin, pin, pin::Pin, time::Duration};
use futures_channel::{mpsc, oneshot};
use futures_core::{
future::{FusedFuture, Future},
stream::{FusedStream, Stream},
task,
task::Poll,
};
use crate::{ControlFlow, MainContext, Priority, Source};
// rustdoc-stripper-ignore-next
/// Represents a `Future` around a `glib::Source`. The future will
/// be resolved once the source has provided a value
pub struct SourceFuture<F, T> {
create_source: Option<F>,
source: Option<(Source, oneshot::Receiver<T>)>,
}
impl<F, T: 'static> SourceFuture<F, T>
where
F: FnOnce(oneshot::Sender<T>) -> Source + 'static,
{
// rustdoc-stripper-ignore-next
/// Create a new `SourceFuture`
///
/// The provided closure should return a newly created `glib::Source` when called
/// and pass the value provided by the source to the oneshot sender that is passed
/// to the closure.
pub fn new(create_source: F) -> SourceFuture<F, T> {
SourceFuture {
create_source: Some(create_source),
source: None,
}
}
}
impl<F, T> Unpin for SourceFuture<F, T> {}
impl<F, T> Future for SourceFuture<F, T>
where
F: FnOnce(oneshot::Sender<T>) -> Source + 'static,
{
type Output = T;
fn poll(mut self: pin::Pin<&mut Self>, ctx: &mut task::Context) -> Poll<T> {
let SourceFuture {
ref mut create_source,
ref mut source,
..
} = *self;
if let Some(create_source) = create_source.take() {
let main_context = MainContext::ref_thread_default();
assert!(
main_context.is_owner(),
"Spawning futures only allowed if the thread is owning the MainContext"
);
// Channel for sending back the Source result to our future here.
//
// In theory, we could directly continue polling the
// corresponding task from the Source callback,
// however this would break at the very least
// the g_main_current_source() API.
let (send, recv) = oneshot::channel();
let s = create_source(send);
s.attach(Some(&main_context));
*source = Some((s, recv));
}
// At this point we must have a receiver
let res = {
let &mut (_, ref mut receiver) = source.as_mut().unwrap();
Pin::new(receiver).poll(ctx)
};
#[allow(clippy::match_wild_err_arm)]
match res {
Poll::Ready(Err(_)) => panic!("Source sender was unexpectedly closed"),
Poll::Ready(Ok(v)) => {
// Get rid of the reference to the source, it triggered
let _ = source.take();
Poll::Ready(v)
}
Poll::Pending => Poll::Pending,
}
}
}
impl<F, T> FusedFuture for SourceFuture<F, T>
where
F: FnOnce(oneshot::Sender<T>) -> Source + 'static,
{
fn is_terminated(&self) -> bool {
self.create_source.is_none()
&& self
.source
.as_ref()
.map_or(true, |(_, receiver)| receiver.is_terminated())
}
}
impl<T, F> Drop for SourceFuture<T, F> {
fn drop(&mut self) {
// Get rid of the source, we don't care anymore if it still triggers
if let Some((source, _)) = self.source.take() {
source.destroy();
}
}
}
// rustdoc-stripper-ignore-next
/// Create a `Future` that will resolve after the given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future(value: Duration) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
timeout_future_with_priority(crate::Priority::default(), value)
}
// rustdoc-stripper-ignore-next
/// Create a `Future` that will resolve after the given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future_with_priority(
priority: Priority,
value: Duration,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
Box::pin(SourceFuture::new(move |send| {
let mut send = Some(send);
crate::timeout_source_new(value, None, priority, move || {
let _ = send.take().unwrap().send(());
ControlFlow::Break
})
}))
}
// rustdoc-stripper-ignore-next
/// Create a `Future` that will resolve after the given number of seconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future_seconds(value: u32) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
timeout_future_seconds_with_priority(crate::Priority::default(), value)
}
// rustdoc-stripper-ignore-next
/// Create a `Future` that will resolve after the given number of seconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future_seconds_with_priority(
priority: Priority,
value: u32,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
Box::pin(SourceFuture::new(move |send| {
let mut send = Some(send);
crate::timeout_source_new_seconds(value, None, priority, move || {
let _ = send.take().unwrap().send(());
ControlFlow::Break
})
}))
}
// rustdoc-stripper-ignore-next
/// Create a `Future` that will resolve once the child process with the given pid exits
///
/// The `Future` will resolve to the pid of the child process and the exit code.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn child_watch_future(
pid: crate::Pid,
) -> Pin<Box<dyn Future<Output = (crate::Pid, i32)> + Send + 'static>> {
child_watch_future_with_priority(crate::Priority::default(), pid)
}
// rustdoc-stripper-ignore-next
/// Create a `Future` that will resolve once the child process with the given pid exits
///
/// The `Future` will resolve to the pid of the child process and the exit code.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn child_watch_future_with_priority(
priority: Priority,
pid: crate::Pid,
) -> Pin<Box<dyn Future<Output = (crate::Pid, i32)> + Send + 'static>> {
Box::pin(SourceFuture::new(move |send| {
let mut send = Some(send);
crate::child_watch_source_new(pid, None, priority, move |pid, code| {
let _ = send.take().unwrap().send((pid, code));
})
}))
}
#[cfg(any(unix, docsrs))]
#[cfg_attr(docsrs, doc(cfg(unix)))]
// rustdoc-stripper-ignore-next
/// Create a `Future` that will resolve once the given UNIX signal is raised
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_future(signum: i32) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
unix_signal_future_with_priority(crate::Priority::default(), signum)
}
#[cfg(any(unix, docsrs))]
#[cfg_attr(docsrs, doc(cfg(unix)))]
// rustdoc-stripper-ignore-next
/// Create a `Future` that will resolve once the given UNIX signal is raised
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_future_with_priority(
priority: Priority,
signum: i32,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
Box::pin(SourceFuture::new(move |send| {
let mut send = Some(send);
crate::unix_signal_source_new(signum, None, priority, move || {
let _ = send.take().unwrap().send(());
ControlFlow::Break
})
}))
}
// rustdoc-stripper-ignore-next
/// Represents a `Stream` around a `glib::Source`. The stream will
/// be provide all values that are provided by the source
pub struct SourceStream<F, T> {
create_source: Option<F>,
source: Option<(Source, mpsc::UnboundedReceiver<T>)>,
}
impl<F, T> Unpin for SourceStream<F, T> {}
impl<F, T: 'static> SourceStream<F, T>
where
F: FnOnce(mpsc::UnboundedSender<T>) -> Source + 'static,
{
// rustdoc-stripper-ignore-next
/// Create a new `SourceStream`
///
/// The provided closure should return a newly created `glib::Source` when called
/// and pass the values provided by the source to the sender that is passed
/// to the closure.
pub fn new(create_source: F) -> SourceStream<F, T> {
SourceStream {
create_source: Some(create_source),
source: None,
}
}
}
impl<F, T> Stream for SourceStream<F, T>
where
F: FnOnce(mpsc::UnboundedSender<T>) -> Source + 'static,
{
type Item = T;
fn poll_next(mut self: pin::Pin<&mut Self>, ctx: &mut task::Context) -> Poll<Option<T>> {
let SourceStream {
ref mut create_source,
ref mut source,
..
} = *self;
if let Some(create_source) = create_source.take() {
let main_context = MainContext::ref_thread_default();
assert!(
main_context.is_owner(),
"Spawning futures only allowed if the thread is owning the MainContext"
);
// Channel for sending back the Source result to our future here.
//
// In theory we could directly continue polling the
// corresponding task from the Source callback,
// however this would break at the very least
// the g_main_current_source() API.
let (send, recv) = mpsc::unbounded();
let s = create_source(send);
s.attach(Some(&main_context));
*source = Some((s, recv));
}
// At this point we must have a receiver
let res = {
let &mut (_, ref mut receiver) = source.as_mut().unwrap();
Pin::new(receiver).poll_next(ctx)
};
#[allow(clippy::match_wild_err_arm)]
match res {
Poll::Ready(v) => {
if v.is_none() {
// Get rid of the reference to the source, it triggered
let _ = source.take();
}
Poll::Ready(v)
}
Poll::Pending => Poll::Pending,
}
}
}
impl<F, T> FusedStream for SourceStream<F, T>
where
F: FnOnce(mpsc::UnboundedSender<T>) -> Source + 'static,
{
fn is_terminated(&self) -> bool {
self.create_source.is_none()
&& self
.source
.as_ref()
.map_or(true, |(_, receiver)| receiver.is_terminated())
}
}
impl<T, F> Drop for SourceStream<T, F> {
fn drop(&mut self) {
// Get rid of the source, we don't care anymore if it still triggers
if let Some((source, _)) = self.source.take() {
source.destroy();
}
}
}
// rustdoc-stripper-ignore-next
/// Create a `Stream` that will provide a value every given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream(value: Duration) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
interval_stream_with_priority(crate::Priority::default(), value)
}
// rustdoc-stripper-ignore-next
/// Create a `Stream` that will provide a value every given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream_with_priority(
priority: Priority,
value: Duration,
) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
Box::pin(SourceStream::new(move |send| {
crate::timeout_source_new(value, None, priority, move || {
if send.unbounded_send(()).is_err() {
ControlFlow::Break
} else {
ControlFlow::Continue
}
})
}))
}
// rustdoc-stripper-ignore-next
/// Create a `Stream` that will provide a value every given number of seconds.
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream_seconds(value: u32) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
interval_stream_seconds_with_priority(crate::Priority::default(), value)
}
// rustdoc-stripper-ignore-next
/// Create a `Stream` that will provide a value every given number of seconds.
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream_seconds_with_priority(
priority: Priority,
value: u32,
) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
Box::pin(SourceStream::new(move |send| {
crate::timeout_source_new_seconds(value, None, priority, move || {
if send.unbounded_send(()).is_err() {
ControlFlow::Break
} else {
ControlFlow::Continue
}
})
}))
}
#[cfg(any(unix, docsrs))]
#[cfg_attr(docsrs, doc(cfg(unix)))]
// rustdoc-stripper-ignore-next
/// Create a `Stream` that will provide a value whenever the given UNIX signal is raised
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_stream(signum: i32) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
unix_signal_stream_with_priority(crate::Priority::default(), signum)
}
#[cfg(any(unix, docsrs))]
#[cfg_attr(docsrs, doc(cfg(unix)))]
// rustdoc-stripper-ignore-next
/// Create a `Stream` that will provide a value whenever the given UNIX signal is raised
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_stream_with_priority(
priority: Priority,
signum: i32,
) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
Box::pin(SourceStream::new(move |send| {
crate::unix_signal_source_new(signum, None, priority, move || {
if send.unbounded_send(()).is_err() {
ControlFlow::Break
} else {
ControlFlow::Continue
}
})
}))
}
#[cfg(test)]
mod tests {
use std::{thread, time::Duration};
use futures_util::{future::FutureExt, stream::StreamExt};
use super::*;
#[test]
fn test_timeout() {
let c = MainContext::new();
c.block_on(timeout_future(Duration::from_millis(20)));
}
#[test]
fn test_timeout_send() {
let c = MainContext::new();
let l = crate::MainLoop::new(Some(&c), false);
let l_clone = l.clone();
c.spawn(timeout_future(Duration::from_millis(20)).then(move |()| {
l_clone.quit();
futures_util::future::ready(())
}));
l.run();
}
#[test]
fn test_interval() {
let c = MainContext::new();
let mut count = 0;
{
let count = &mut count;
c.block_on(
interval_stream(Duration::from_millis(20))
.take(2)
.for_each(|()| {
*count += 1;
futures_util::future::ready(())
})
.map(|_| ()),
);
}
assert_eq!(count, 2);
}
#[test]
fn test_timeout_and_channel() {
let c = MainContext::default();
let res = c.block_on(timeout_future(Duration::from_millis(20)).then(|()| {
let (sender, receiver) = oneshot::channel();
thread::spawn(move || {
sender.send(1).unwrap();
});
receiver.then(|i| futures_util::future::ready(i.unwrap()))
}));
assert_eq!(res, 1);
}
}
@@ -0,0 +1,72 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! This module contains basic instance and class structs to be used for
//! `GObject` subclasses that don't require any additional data in these
//! structs and don't provide any new virtual methods.
use std::{fmt, ops};
use super::prelude::*;
use crate::prelude::*;
// rustdoc-stripper-ignore-next
/// A basic instance struct that does not store any additional data.
#[repr(C)]
pub struct InstanceStruct<T: ObjectSubclass> {
parent: <T::ParentType as ObjectType>::GlibType,
}
impl<T: ObjectSubclass> fmt::Debug for InstanceStruct<T>
where
<T::ParentType as ObjectType>::GlibType: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("InstanceStruct")
.field("parent", &self.parent)
.finish()
}
}
unsafe impl<T: ObjectSubclass> super::types::InstanceStruct for InstanceStruct<T> {
type Type = T;
}
// rustdoc-stripper-ignore-next
/// A basic class struct that does not store any additional data
/// or virtual methods.
#[repr(C)]
pub struct ClassStruct<T: ObjectSubclass> {
parent_class: <T::ParentType as ObjectType>::GlibClassType,
}
impl<T: ObjectSubclass> fmt::Debug for ClassStruct<T>
where
<T::ParentType as ObjectType>::GlibClassType: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("InstanceStruct")
.field("parent_class", &self.parent_class)
.finish()
}
}
unsafe impl<T: ObjectSubclass> super::types::ClassStruct for ClassStruct<T> {
type Type = T;
}
impl<T: ObjectSubclass> ops::Deref for ClassStruct<T> {
type Target = crate::Class<<T as ObjectSubclass>::Type>;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &*(self as *const _ as *const Self::Target) }
}
}
impl<T: ObjectSubclass> ops::DerefMut for ClassStruct<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self as *mut _ as *mut Self::Target) }
}
}
+109
View File
@@ -0,0 +1,109 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! Module for registering boxed types for Rust types.
use crate::{prelude::*, translate::*};
// rustdoc-stripper-ignore-next
/// Trait for defining boxed types.
///
/// Links together the type name with the type itself.
///
/// See [`register_boxed_type`] for registering an implementation of this trait
/// with the type system.
///
/// [`register_boxed_type`]: fn.register_boxed_type.html
pub trait BoxedType: StaticType + Clone + Sized + 'static {
// rustdoc-stripper-ignore-next
/// Boxed type name.
///
/// This must be unique in the whole process.
const NAME: &'static str;
}
// rustdoc-stripper-ignore-next
/// Register a boxed `glib::Type` ID for `T`.
///
/// This must be called only once and will panic on a second call.
///
/// See [`Boxed!`] for defining a function that ensures that
/// this is only called once and returns the type id.
///
/// [`Boxed!`]: ../../derive.Boxed.html
pub fn register_boxed_type<T: BoxedType>() -> crate::Type {
unsafe extern "C" fn boxed_copy<T: BoxedType>(v: ffi::gpointer) -> ffi::gpointer {
let v = &*(v as *mut T);
let copy = Box::new(v.clone());
Box::into_raw(copy) as ffi::gpointer
}
unsafe extern "C" fn boxed_free<T: BoxedType>(v: ffi::gpointer) {
let v = v as *mut T;
let _ = Box::from_raw(v);
}
unsafe {
use std::ffi::CString;
let type_name = CString::new(T::NAME).unwrap();
assert_eq!(
gobject_ffi::g_type_from_name(type_name.as_ptr()),
gobject_ffi::G_TYPE_INVALID,
"Type {} has already been registered",
type_name.to_str().unwrap()
);
let type_ = crate::Type::from_glib(gobject_ffi::g_boxed_type_register_static(
type_name.as_ptr(),
Some(boxed_copy::<T>),
Some(boxed_free::<T>),
));
assert!(type_.is_valid());
type_
}
}
#[cfg(test)]
mod test {
// We rename the current crate as glib, since the macros in glib-macros
// generate the glib namespace through the crate_ident_new utility,
// and that returns `glib` (and not `crate`) when called inside the glib crate
use crate as glib;
use crate::prelude::*;
use crate::translate::{FromGlibPtrBorrow, FromGlibPtrFull, IntoGlibPtr};
#[derive(Clone, Debug, PartialEq, Eq, glib::Boxed)]
#[boxed_type(name = "MyBoxed")]
struct MyBoxed(String);
#[test]
fn test_register() {
assert!(MyBoxed::static_type().is_valid());
}
#[test]
fn test_value() {
assert!(MyBoxed::static_type().is_valid());
let b = MyBoxed(String::from("abc"));
let v = b.to_value();
let b2 = v.get::<&MyBoxed>().unwrap();
assert_eq!(&b, b2);
}
#[test]
fn test_from_glib_borrow() {
assert!(MyBoxed::static_type().is_valid());
let b = MyBoxed(String::from("abc"));
let raw_ptr = unsafe { MyBoxed::into_glib_ptr(b) };
// test that the from_glib_borrow does not take ownership of the raw_ptr
let _ = unsafe { MyBoxed::from_glib_borrow(raw_ptr) };
let new_b = unsafe { MyBoxed::from_glib_full(raw_ptr) };
assert_eq!(new_b.0, "abc".to_string());
}
}
@@ -0,0 +1,217 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{marker, mem};
use super::{InitializingType, Signal};
use crate::{prelude::*, translate::*, Object, ParamSpec, Type};
// rustdoc-stripper-ignore-next
/// Trait for a type list of prerequisite object types.
pub trait PrerequisiteList {
// rustdoc-stripper-ignore-next
/// Returns the list of types for this list.
fn types() -> Vec<ffi::GType>;
}
impl PrerequisiteList for () {
fn types() -> Vec<ffi::GType> {
vec![]
}
}
impl<T: crate::ObjectType> PrerequisiteList for (T,) {
fn types() -> Vec<ffi::GType> {
vec![T::static_type().into_glib()]
}
}
// Generates all the PrerequisiteList impls for prerequisite_lists of arbitrary sizes based on a list of type
// parameters like A B C. It would generate the impl then for (A, B) and (A, B, C).
macro_rules! prerequisite_list_trait(
($name1:ident, $name2: ident, $($name:ident),*) => (
prerequisite_list_trait!(__impl $name1, $name2; $($name),*);
);
(__impl $($name:ident),+; $name1:ident, $($name2:ident),*) => (
prerequisite_list_trait_impl!($($name),+);
prerequisite_list_trait!(__impl $($name),+ , $name1; $($name2),*);
);
(__impl $($name:ident),+; $name1:ident) => (
prerequisite_list_trait_impl!($($name),+);
prerequisite_list_trait_impl!($($name),+, $name1);
);
);
// Generates the impl block for PrerequisiteList on prerequisite_lists or arbitrary sizes based on its
// arguments. Takes a list of type parameters as parameters, e.g. A B C
// and then implements the trait on (A, B, C).
macro_rules! prerequisite_list_trait_impl(
($($name:ident),+) => (
impl<$($name: crate::ObjectType),+> PrerequisiteList for ( $($name),+ ) {
fn types() -> Vec<ffi::GType> {
vec![$($name::static_type().into_glib()),+]
}
}
);
);
prerequisite_list_trait!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
/// Type methods required for an [`ObjectInterface`] implementation.
///
/// This is usually generated by the [`#[object_interface]`](crate::object_interface) attribute macro.
pub unsafe trait ObjectInterfaceType {
/// Returns the `glib::Type` ID of the interface.
///
/// This will register the type with the type system on the first call.
#[doc(alias = "get_type")]
fn type_() -> Type;
}
/// The central trait for defining a `GObject` interface.
///
/// Links together the type name, and the interface struct for type registration and allows hooking
/// into various steps of the type registration and initialization.
///
/// This must only be implemented on `#[repr(C)]` structs and have `gobject_ffi::GTypeInterface` as
/// the first field.
///
/// See [`register_interface`] for registering an implementation of this trait
/// with the type system.
///
/// [`register_interface`]: fn.register_interface.html
pub unsafe trait ObjectInterface: ObjectInterfaceType + Sized + 'static {
/// `GObject` type name.
///
/// This must be unique in the whole process.
const NAME: &'static str;
/// Prerequisites for this interface.
///
/// Any implementer of the interface must be a subclass of the prerequisites or implement them
/// in case of interfaces.
type Prerequisites: PrerequisiteList;
/// Additional type initialization.
///
/// This is called right after the type was registered and allows
/// interfaces to do additional type-specific initialization.
///
/// Optional
fn type_init(_type_: &mut InitializingType<Self>) {}
/// Interface initialization.
///
/// This is called after `type_init` and before the first implementor
/// of the interface is created. Interfaces can use this to do interface-
/// specific initialization, e.g. for installing signals on the interface,
/// and for setting default implementations of interface functions.
///
/// Optional
fn interface_init(&mut self) {}
/// Properties installed for this interface.
///
/// All implementors of the interface must provide these properties.
fn properties() -> &'static [ParamSpec] {
&[]
}
/// Signals installed for this interface.
fn signals() -> &'static [Signal] {
&[]
}
}
pub trait ObjectInterfaceExt: ObjectInterface {
/// Get interface from an instance.
///
/// This will panic if `obj` does not implement the interface.
#[inline]
#[deprecated = "Use from_obj() instead"]
fn from_instance<T: IsA<Object>>(obj: &T) -> &Self {
Self::from_obj(obj)
}
/// Get interface from an instance.
///
/// This will panic if `obj` does not implement the interface.
#[inline]
fn from_obj<T: IsA<Object>>(obj: &T) -> &Self {
assert!(obj.as_ref().type_().is_a(Self::type_()));
unsafe {
let klass = (*(obj.as_ptr() as *const gobject_ffi::GTypeInstance)).g_class;
let interface =
gobject_ffi::g_type_interface_peek(klass as *mut _, Self::type_().into_glib());
debug_assert!(!interface.is_null());
&*(interface as *const Self)
}
}
}
impl<T: ObjectInterface> ObjectInterfaceExt for T {}
unsafe extern "C" fn interface_init<T: ObjectInterface>(
klass: ffi::gpointer,
_klass_data: ffi::gpointer,
) {
let iface = &mut *(klass as *mut T);
let pspecs = <T as ObjectInterface>::properties();
for pspec in pspecs {
gobject_ffi::g_object_interface_install_property(
iface as *mut T as *mut _,
pspec.to_glib_none().0,
);
}
let type_ = T::type_();
let signals = <T as ObjectInterface>::signals();
for signal in signals {
signal.register(type_);
}
iface.interface_init();
}
/// Register a `glib::Type` ID for `T`.
///
/// This must be called only once and will panic on a second call.
///
/// The [`object_interface!`] macro will create a `type_()` function around this, which will
/// ensure that it's only ever called once.
///
/// [`object_interface!`]: ../../macro.object_interface.html
pub fn register_interface<T: ObjectInterface>() -> Type {
unsafe {
use std::ffi::CString;
let type_name = CString::new(T::NAME).unwrap();
assert_eq!(
gobject_ffi::g_type_from_name(type_name.as_ptr()),
gobject_ffi::G_TYPE_INVALID
);
let type_ = gobject_ffi::g_type_register_static_simple(
Type::INTERFACE.into_glib(),
type_name.as_ptr(),
mem::size_of::<T>() as u32,
Some(interface_init::<T>),
0,
None,
0,
);
let prerequisites = T::Prerequisites::types();
for prerequisite in prerequisites {
gobject_ffi::g_type_interface_add_prerequisite(type_, prerequisite);
}
let type_ = Type::from_glib(type_);
assert!(type_.is_valid());
T::type_init(&mut InitializingType::<T>(type_, marker::PhantomData));
type_
}
}
+267
View File
@@ -0,0 +1,267 @@
// Take a look at the license at the top of the repository in the LICENSE file.
#![allow(clippy::needless_doctest_main)]
// rustdoc-stripper-ignore-next
//! Module containing infrastructure for subclassing `GObject`s and registering boxed types.
//!
//! # Example for registering a `glib::Object` subclass
//!
//! The following code implements a subclass of `glib::Object` with a
//! string-typed "name" property.
//!
//! ```rust
//! use glib::prelude::*;
//! use glib::subclass;
//! use glib::subclass::prelude::*;
//! use glib::{Variant, VariantType};
//!
//! use std::cell::{Cell, RefCell};
//!
//! #[derive(Debug, Eq, PartialEq, Clone, Copy, glib::Enum)]
//! #[repr(u32)]
//! // type_name: GType name of the enum (mandatory)
//! #[enum_type(name = "SimpleObjectAnimal")]
//! enum Animal {
//! Goat = 0,
//! #[enum_value(name = "The Dog")]
//! Dog = 1,
//! // name: the name of the GEnumValue (optional), default to the enum name in CamelCase
//! // nick: the nick of the GEnumValue (optional), default to the enum name in kebab-case
//! #[enum_value(name = "The Cat", nick = "chat")]
//! Cat = 2,
//! }
//!
//! impl Default for Animal {
//! fn default() -> Self {
//! Animal::Goat
//! }
//! }
//!
//! #[glib::flags(name = "MyFlags")]
//! enum MyFlags {
//! #[flags_value(name = "Flag A", nick = "nick-a")]
//! A = 0b00000001,
//! #[flags_value(name = "Flag B")]
//! B = 0b00000010,
//! #[flags_value(skip)]
//! AB = Self::A.bits() | Self::B.bits(),
//! C = 0b00000100,
//! }
//!
//! impl Default for MyFlags {
//! fn default() -> Self {
//! MyFlags::A
//! }
//! }
//!
//! mod imp {
//! use super::*;
//!
//! // This is the struct containing all state carried with
//! // the new type. Generally this has to make use of
//! // interior mutability.
//! // If it implements the `Default` trait, then `Self::default()`
//! // will be called every time a new instance is created.
//! #[derive(Default)]
//! pub struct SimpleObject {
//! name: RefCell<Option<String>>,
//! animal: Cell<Animal>,
//! flags: Cell<MyFlags>,
//! variant: RefCell<Option<Variant>>,
//! }
//!
//! // ObjectSubclass is the trait that defines the new type and
//! // contains all information needed by the GObject type system,
//! // including the new type's name, parent type, etc.
//! // If you do not want to implement `Default`, you can provide
//! // a `new()` method.
//! #[glib::object_subclass]
//! impl ObjectSubclass for SimpleObject {
//! // This type name must be unique per process.
//! const NAME: &'static str = "SimpleObject";
//!
//! type Type = super::SimpleObject;
//!
//! // The parent type this one is inheriting from.
//! // Optional, if not specified it defaults to `glib::Object`
//! type ParentType = glib::Object;
//!
//! // Interfaces this type implements.
//! // Optional, if not specified it defaults to `()`
//! type Interfaces = ();
//! }
//!
//! // Trait that is used to override virtual methods of glib::Object.
//! impl ObjectImpl for SimpleObject {
//! // Called once in the very beginning to list all properties of this class.
//! fn properties() -> &'static [glib::ParamSpec] {
//! use once_cell::sync::Lazy;
//! static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
//! vec![
//! glib::ParamSpecString::builder("name")
//! .build(),
//! glib::ParamSpecEnum::builder::<Animal>("animal")
//! .build(),
//! glib::ParamSpecFlags::builder::<MyFlags>("flags")
//! .build(),
//! glib::ParamSpecVariant::builder("variant", glib::VariantTy::ANY)
//! .build(),
//! ]
//! });
//!
//! PROPERTIES.as_ref()
//! }
//!
//! // Called whenever a property is set on this instance. The id
//! // is the same as the index of the property in the PROPERTIES array.
//! fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
//! match pspec.name() {
//! "name" => {
//! let name = value
//! .get()
//! .expect("type conformity checked by `Object::set_property`");
//! self.name.replace(name);
//! },
//! "animal" => {
//! let animal = value
//! .get()
//! .expect("type conformity checked by `Object::set_property`");
//! self.animal.replace(animal);
//! },
//! "flags" => {
//! let flags = value
//! .get()
//! .expect("type conformity checked by `Object::set_property`");
//! self.flags.replace(flags);
//! },
//! "variant" => {
//! let variant = value
//! .get()
//! .expect("type conformity checked by `Object::set_property`");
//! self.variant.replace(variant);
//! },
//! _ => unimplemented!(),
//! }
//! }
//!
//! // Called whenever a property is retrieved from this instance. The id
//! // is the same as the index of the property in the PROPERTIES array.
//! fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
//! match pspec.name() {
//! "name" => self.name.borrow().to_value(),
//! "animal" => self.animal.get().to_value(),
//! "flags" => self.flags.get().to_value(),
//! "variant" => self.variant.borrow().to_value(),
//! _ => unimplemented!(),
//! }
//! }
//!
//! // Called right after construction of the instance.
//! fn constructed(&self) {
//! // Chain up to the parent type's implementation of this virtual
//! // method.
//! self.parent_constructed();
//!
//! // And here we could do our own initialization.
//! }
//! }
//! }
//!
//! // Optionally, define a wrapper type to make it more ergonomic to use from Rust
//! glib::wrapper! {
//! pub struct SimpleObject(ObjectSubclass<imp::SimpleObject>);
//! }
//!
//! impl SimpleObject {
//! // Create an object instance of the new type.
//! pub fn new() -> Self {
//! glib::Object::new()
//! }
//! }
//!
//! pub fn main() {
//! let obj = SimpleObject::new();
//!
//! // Get the name property and change its value.
//! assert_eq!(obj.property::<Option<String>>("name"), None);
//! obj.set_property("name", "test");
//! assert_eq!(&obj.property::<String>("name"), "test");
//!
//! assert_eq!(obj.property::<Animal>("animal"), Animal::Goat);
//! obj.set_property("animal", Animal::Cat);
//! assert_eq!(obj.property::<Animal>("animal"), Animal::Cat);
//!
//! assert_eq!(obj.property::<MyFlags>("flags"), MyFlags::A);
//! obj.set_property("flags", MyFlags::B);
//! assert_eq!(obj.property::<MyFlags>("flags"), MyFlags::B);
//! }
//! ```
//!
//! # Example for registering a boxed type for a Rust struct
//!
//! The following code boxed type for a tuple struct around `String` and uses it in combination
//! with `glib::Value`.
//!
//! ```rust
//! use glib::prelude::*;
//! use glib::subclass;
//! use glib::subclass::prelude::*;
//!
//! #[derive(Clone, Debug, PartialEq, Eq, glib::Boxed)]
//! #[boxed_type(name = "MyBoxed")]
//! struct MyBoxed(String);
//!
//! pub fn main() {
//! assert!(MyBoxed::static_type().is_valid());
//!
//! let b = MyBoxed(String::from("abc"));
//! let v = b.to_value();
//! let b2 = v.get::<&MyBoxed>().unwrap();
//! assert_eq!(&b, b2);
//! }
//! ```
pub mod basic;
#[macro_use]
pub mod types;
#[macro_use]
pub mod interface;
#[macro_use]
pub mod object;
#[macro_use]
pub mod boxed;
pub mod shared;
pub mod signal;
mod object_impl_ref;
pub use object_impl_ref::{ObjectImplRef, ObjectImplWeakRef};
pub mod prelude {
// rustdoc-stripper-ignore-next
//! Prelude that re-exports all important traits from this crate.
pub use super::{
boxed::BoxedType,
interface::{ObjectInterface, ObjectInterfaceExt, ObjectInterfaceType},
object::{DerivedObjectProperties, ObjectClassSubclassExt, ObjectImpl, ObjectImplExt},
shared::{RefCounted, SharedType},
types::{
ClassStruct, InstanceStruct, InstanceStructExt, IsImplementable, IsSubclassable,
IsSubclassableExt, ObjectSubclass, ObjectSubclassExt, ObjectSubclassIsExt,
ObjectSubclassType,
},
};
}
pub use self::{
boxed::register_boxed_type,
interface::register_interface,
signal::{
Signal, SignalClassHandlerToken, SignalId, SignalInvocationHint, SignalQuery, SignalType,
},
types::{register_type, InitializingObject, InitializingType, TypeData},
};
@@ -0,0 +1,717 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! Module that contains all types needed for creating a direct subclass of `GObject`
//! or implementing virtual methods of it.
use std::{mem, ptr};
use super::{prelude::*, Signal};
use crate::{prelude::*, translate::*, Cast, Object, ParamSpec, Slice, Value};
// rustdoc-stripper-ignore-next
/// Trait for implementors of `glib::Object` subclasses.
///
/// This allows overriding the virtual methods of `glib::Object`. Except for
/// `finalize` as implementing `Drop` would allow the same behavior.
pub trait ObjectImpl: ObjectSubclass + ObjectImplExt {
// rustdoc-stripper-ignore-next
/// Properties installed for this type.
fn properties() -> &'static [ParamSpec] {
&[]
}
// rustdoc-stripper-ignore-next
/// Signals installed for this type.
fn signals() -> &'static [Signal] {
&[]
}
// rustdoc-stripper-ignore-next
/// Property setter.
///
/// This is called whenever the property of this specific subclass with the
/// given index is set. The new value is passed as `glib::Value`.
///
/// `value` is guaranteed to be of the correct type for the given property.
fn set_property(&self, _id: usize, _value: &Value, _pspec: &ParamSpec) {
unimplemented!()
}
// rustdoc-stripper-ignore-next
/// Property getter.
///
/// This is called whenever the property value of the specific subclass with the
/// given index should be returned.
///
/// The returned `Value` must be of the correct type for the given property.
#[doc(alias = "get_property")]
fn property(&self, _id: usize, _pspec: &ParamSpec) -> Value {
unimplemented!()
}
// rustdoc-stripper-ignore-next
/// Constructed.
///
/// This is called once construction of the instance is finished.
///
/// Should chain up to the parent class' implementation.
fn constructed(&self) {
self.parent_constructed();
}
// rustdoc-stripper-ignore-next
/// Disposes of the object.
///
/// When `dispose()` ends, the object should not hold any reference to any other member object.
/// The object is also expected to be able to answer client method invocations (with possibly an
/// error code but no memory violation) until it is dropped. `dispose()` can be executed more
/// than once.
fn dispose(&self) {}
// rustdoc-stripper-ignore-next
/// Function to be called when property change is notified for with
/// `self.notify("property")`.
fn notify(&self, pspec: &ParamSpec) {
self.parent_notify(pspec)
}
fn dispatch_properties_changed(&self, pspecs: &[ParamSpec]) {
self.parent_dispatch_properties_changed(pspecs)
}
}
#[doc(alias = "get_property")]
unsafe extern "C" fn property<T: ObjectImpl>(
obj: *mut gobject_ffi::GObject,
id: u32,
value: *mut gobject_ffi::GValue,
pspec: *mut gobject_ffi::GParamSpec,
) {
let instance = &*(obj as *mut T::Instance);
let imp = instance.imp();
let v = imp.property(id as usize, &from_glib_borrow(pspec));
// We first unset the value we get passed in, in case it contained
// any previous data. Then we directly overwrite it with our new
// value, and pass ownership of the contained data to the C GValue
// by forgetting it on the Rust side.
//
// Without this, by using the GValue API, we would have to create
// a copy of the value when setting it on the destination just to
// immediately free the original value afterwards.
gobject_ffi::g_value_unset(value);
let v = mem::ManuallyDrop::new(v);
ptr::write(value, ptr::read(v.to_glib_none().0));
}
unsafe extern "C" fn set_property<T: ObjectImpl>(
obj: *mut gobject_ffi::GObject,
id: u32,
value: *mut gobject_ffi::GValue,
pspec: *mut gobject_ffi::GParamSpec,
) {
let instance = &*(obj as *mut T::Instance);
let imp = instance.imp();
imp.set_property(
id as usize,
&*(value as *mut Value),
&from_glib_borrow(pspec),
);
}
unsafe extern "C" fn constructed<T: ObjectImpl>(obj: *mut gobject_ffi::GObject) {
let instance = &*(obj as *mut T::Instance);
let imp = instance.imp();
imp.constructed();
}
unsafe extern "C" fn notify<T: ObjectImpl>(
obj: *mut gobject_ffi::GObject,
pspec: *mut gobject_ffi::GParamSpec,
) {
let instance = &*(obj as *mut T::Instance);
let imp = instance.imp();
imp.notify(&from_glib_borrow(pspec));
}
unsafe extern "C" fn dispatch_properties_changed<T: ObjectImpl>(
obj: *mut gobject_ffi::GObject,
n_pspecs: u32,
pspecs: *mut *mut gobject_ffi::GParamSpec,
) {
let instance = &*(obj as *mut T::Instance);
let imp = instance.imp();
imp.dispatch_properties_changed(Slice::from_glib_borrow_num(pspecs, n_pspecs as _));
}
unsafe extern "C" fn dispose<T: ObjectImpl>(obj: *mut gobject_ffi::GObject) {
let instance = &*(obj as *mut T::Instance);
let imp = instance.imp();
imp.dispose();
// Chain up to the parent's dispose.
let data = T::type_data();
let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
if let Some(ref func) = (*parent_class).dispose {
func(obj);
}
}
// rustdoc-stripper-ignore-next
/// Trait containing only the property related functions of `ObjectImpl`.
/// Implemented by the `Props` macro.
/// When implementing `ObjectImpl` you may want to delegate the function calls to this trait.
pub trait DerivedObjectProperties: ObjectSubclass {
// rustdoc-stripper-ignore-next
/// Properties installed for this type.
fn derived_properties() -> &'static [ParamSpec] {
&[]
}
// rustdoc-stripper-ignore-next
/// Similar to [`ObjectImpl`](trait.ObjectImpl.html) but auto-generated by the [`Properties`] macro
/// to allow handling more complex use-cases.
fn derived_set_property(&self, _id: usize, _value: &Value, _pspec: &ParamSpec) {
unimplemented!()
}
// rustdoc-stripper-ignore-next
/// Similar to [`ObjectImpl`](trait.ObjectImpl.html) but auto-generated by the [`Properties`] macro
/// to allow handling more complex use-cases.
fn derived_property(&self, _id: usize, _pspec: &ParamSpec) -> Value {
unimplemented!()
}
}
// rustdoc-stripper-ignore-next
/// Extension trait for `glib::Object`'s class struct.
///
/// This contains various class methods and allows subclasses to override signal class handlers.
pub unsafe trait ObjectClassSubclassExt: Sized + 'static {
fn override_signal_class_handler<F>(&mut self, name: &str, class_handler: F)
where
F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
{
unsafe {
super::types::signal_override_class_handler(
name,
*(self as *mut _ as *mut ffi::GType),
class_handler,
);
}
}
}
unsafe impl ObjectClassSubclassExt for crate::Class<Object> {}
unsafe impl<T: ObjectImpl> IsSubclassable<T> for Object {
fn class_init(class: &mut crate::Class<Self>) {
let klass = class.as_mut();
klass.set_property = Some(set_property::<T>);
klass.get_property = Some(property::<T>);
klass.constructed = Some(constructed::<T>);
klass.notify = Some(notify::<T>);
klass.dispatch_properties_changed = Some(dispatch_properties_changed::<T>);
klass.dispose = Some(dispose::<T>);
let pspecs = <T as ObjectImpl>::properties();
if !pspecs.is_empty() {
unsafe {
let mut pspecs_ptrs = Vec::with_capacity(pspecs.len() + 1);
pspecs_ptrs.push(ptr::null_mut());
for pspec in pspecs {
pspecs_ptrs.push(pspec.to_glib_none().0);
}
gobject_ffi::g_object_class_install_properties(
klass,
pspecs_ptrs.len() as u32,
pspecs_ptrs.as_mut_ptr(),
);
}
}
let type_ = T::type_();
let signals = <T as ObjectImpl>::signals();
for signal in signals {
signal.register(type_);
}
}
#[inline]
fn instance_init(_instance: &mut super::InitializingObject<T>) {}
}
mod sealed {
pub trait Sealed {}
impl<T: super::ObjectImplExt> Sealed for T {}
}
pub trait ObjectImplExt: sealed::Sealed + ObjectSubclass {
// rustdoc-stripper-ignore-next
/// Chain up to the parent class' implementation of `glib::Object::constructed()`.
#[inline]
fn parent_constructed(&self) {
unsafe {
let data = Self::type_data();
let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
if let Some(ref func) = (*parent_class).constructed {
func(self.obj().unsafe_cast_ref::<Object>().to_glib_none().0);
}
}
}
// rustdoc-stripper-ignore-next
/// Chain up to the parent class' implementation of `glib::Object::notify()`.
#[inline]
fn parent_notify(&self, pspec: &ParamSpec) {
unsafe {
let data = Self::type_data();
let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
if let Some(ref func) = (*parent_class).notify {
func(
self.obj().unsafe_cast_ref::<Object>().to_glib_none().0,
pspec.to_glib_none().0,
);
}
}
}
// rustdoc-stripper-ignore-next
/// Chain up to the parent class' implementation of `glib::Object::dispatch_properties_changed()`.
#[inline]
fn parent_dispatch_properties_changed(&self, pspecs: &[ParamSpec]) {
unsafe {
let data = Self::type_data();
let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
if let Some(ref func) = (*parent_class).dispatch_properties_changed {
func(
self.obj().unsafe_cast_ref::<Object>().to_glib_none().0,
pspecs.len() as _,
pspecs.as_ptr() as *mut _,
);
}
}
}
// rustdoc-stripper-ignore-next
/// Chain up to parent class signal handler.
fn signal_chain_from_overridden(
&self,
token: &super::SignalClassHandlerToken,
values: &[Value],
) -> Option<Value> {
unsafe {
super::types::signal_chain_from_overridden(self.obj().as_ptr() as *mut _, token, values)
}
}
}
impl<T: ObjectImpl> ObjectImplExt for T {}
#[cfg(test)]
mod test {
use std::cell::RefCell;
use super::*;
// We rename the current crate as glib, since the macros in glib-macros
// generate the glib namespace through the crate_ident_new utility,
// and that returns `glib` (and not `crate`) when called inside the glib crate
use crate as glib;
mod imp {
use super::*;
// A dummy `Object` to test setting an `Object` property and returning an `Object` in signals
#[derive(Default)]
pub struct ChildObject;
#[glib::object_subclass]
impl ObjectSubclass for ChildObject {
const NAME: &'static str = "ChildObject";
type Type = super::ChildObject;
}
impl ObjectImpl for ChildObject {}
#[derive(Default)]
pub struct SimpleObject {
name: RefCell<Option<String>>,
construct_name: RefCell<Option<String>>,
constructed: RefCell<bool>,
}
#[glib::object_subclass]
impl ObjectSubclass for SimpleObject {
const NAME: &'static str = "SimpleObject";
type Type = super::SimpleObject;
type Interfaces = (super::Dummy,);
}
impl ObjectImpl for SimpleObject {
fn properties() -> &'static [ParamSpec] {
use once_cell::sync::Lazy;
static PROPERTIES: Lazy<Vec<ParamSpec>> = Lazy::new(|| {
vec![
crate::ParamSpecString::builder("name").build(),
crate::ParamSpecString::builder("construct-name")
.construct_only()
.build(),
crate::ParamSpecBoolean::builder("constructed")
.read_only()
.build(),
crate::ParamSpecObject::builder::<super::ChildObject>("child").build(),
]
});
PROPERTIES.as_ref()
}
fn signals() -> &'static [super::Signal] {
use once_cell::sync::Lazy;
static SIGNALS: Lazy<Vec<super::Signal>> = Lazy::new(|| {
vec![
super::Signal::builder("name-changed")
.param_types([String::static_type()])
.build(),
super::Signal::builder("change-name")
.param_types([String::static_type()])
.return_type::<String>()
.action()
.class_handler(|_, args| {
let obj = args[0]
.get::<super::SimpleObject>()
.expect("Failed to get Object from args[0]");
let new_name = args[1]
.get::<String>()
.expect("Failed to get Object from args[1]");
let imp = obj.imp();
let old_name = imp.name.replace(Some(new_name));
obj.emit_by_name::<()>("name-changed", &[&*imp.name.borrow()]);
Some(old_name.to_value())
})
.build(),
super::Signal::builder("create-string")
.return_type::<String>()
.build(),
super::Signal::builder("create-child-object")
.return_type::<super::ChildObject>()
.build(),
]
});
SIGNALS.as_ref()
}
fn set_property(&self, _id: usize, value: &Value, pspec: &crate::ParamSpec) {
match pspec.name() {
"name" => {
let name = value
.get()
.expect("type conformity checked by 'Object::set_property'");
self.name.replace(name);
self.obj()
.emit_by_name::<()>("name-changed", &[&*self.name.borrow()]);
}
"construct-name" => {
let name = value
.get()
.expect("type conformity checked by 'Object::set_property'");
self.construct_name.replace(name);
}
"child" => {
// not stored, only used to test `set_property` with `Objects`
}
_ => unimplemented!(),
}
}
fn property(&self, _id: usize, pspec: &crate::ParamSpec) -> Value {
match pspec.name() {
"name" => self.name.borrow().to_value(),
"construct-name" => self.construct_name.borrow().to_value(),
"constructed" => self.constructed.borrow().to_value(),
_ => unimplemented!(),
}
}
fn constructed(&self) {
self.parent_constructed();
debug_assert_eq!(self as *const _, self.obj().imp() as *const _);
*self.constructed.borrow_mut() = true;
}
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct DummyInterface {
parent: gobject_ffi::GTypeInterface,
}
#[glib::object_interface]
unsafe impl ObjectInterface for DummyInterface {
const NAME: &'static str = "Dummy";
}
}
wrapper! {
pub struct ChildObject(ObjectSubclass<imp::ChildObject>);
}
wrapper! {
pub struct SimpleObject(ObjectSubclass<imp::SimpleObject>);
}
wrapper! {
pub struct Dummy(ObjectInterface<imp::DummyInterface>);
}
unsafe impl<T: ObjectSubclass> IsImplementable<T> for Dummy {}
#[test]
fn test_create() {
let type_ = SimpleObject::static_type();
let obj = Object::with_type(type_);
assert!(obj.type_().is_a(Dummy::static_type()));
// Assert that the object representation is equivalent to the underlying C GObject pointer
assert_eq!(
mem::size_of::<SimpleObject>(),
mem::size_of::<ffi::gpointer>()
);
assert_eq!(obj.as_ptr() as ffi::gpointer, unsafe {
*(&obj as *const _ as *const ffi::gpointer)
});
assert!(obj.property::<bool>("constructed"));
let weak = obj.downgrade();
drop(obj);
assert!(weak.upgrade().is_none());
}
#[test]
fn test_properties() {
let type_ = SimpleObject::static_type();
let obj = Object::with_type(type_);
assert!(obj.type_().is_a(Dummy::static_type()));
let properties = obj.list_properties();
assert_eq!(properties.len(), 4);
assert_eq!(properties[0].name(), "name");
assert_eq!(properties[1].name(), "construct-name");
assert_eq!(properties[2].name(), "constructed");
assert_eq!(properties[3].name(), "child");
}
#[test]
fn test_create_child_object() {
let obj: ChildObject = Object::new();
assert_eq!(&obj, obj.imp().obj().as_ref());
}
#[test]
fn test_builder() {
let obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("name", "initial")
.build();
assert_eq!(
obj.property::<String>("construct-name"),
String::from("meh")
);
assert_eq!(obj.property::<String>("name"), String::from("initial"));
}
#[test]
fn test_set_property() {
let obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("name", "initial")
.build();
assert_eq!(
obj.property::<String>("construct-name"),
String::from("meh")
);
assert_eq!(
obj.property::<String>("construct-name"),
String::from("meh")
);
assert_eq!(obj.property::<String>("name"), String::from("initial"));
obj.set_property("name", "test");
assert_eq!(obj.property::<String>("name"), String::from("test"));
let child = Object::with_type(ChildObject::static_type());
obj.set_property("child", &child);
}
#[test]
#[should_panic = "property 'construct-name' of type 'SimpleObject' is not writable"]
fn test_set_property_non_writable() {
let obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("name", "initial")
.build();
obj.set_property("construct-name", "test");
}
#[test]
#[should_panic = "property 'test' of type 'SimpleObject' not found"]
fn test_set_property_not_found() {
let obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("name", "initial")
.build();
obj.set_property("test", true);
}
#[test]
#[should_panic = "property 'constructed' of type 'SimpleObject' is not writable"]
fn test_set_property_not_writable() {
let obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("name", "initial")
.build();
obj.set_property("constructed", false);
}
#[test]
#[should_panic = "property 'name' of type 'SimpleObject' can't be set from the given type (expected: 'gchararray', got: 'gboolean')"]
fn test_set_property_wrong_type() {
let obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("name", "initial")
.build();
obj.set_property("name", false);
}
#[test]
#[should_panic = "property 'child' of type 'SimpleObject' can't be set from the given type (expected: 'ChildObject', got: 'SimpleObject')"]
fn test_set_property_wrong_type_2() {
let obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("name", "initial")
.build();
let other_obj = Object::with_type(SimpleObject::static_type());
obj.set_property("child", &other_obj);
}
#[test]
#[should_panic = "Can't set construct property 'construct-name' for type 'SimpleObject' twice"]
fn test_construct_property_set_twice() {
let _obj = Object::builder::<SimpleObject>()
.property("construct-name", "meh")
.property("construct-name", "meh2")
.build();
}
#[test]
fn test_signals() {
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
let obj = Object::builder::<SimpleObject>()
.property("name", "old-name")
.build();
let name_changed_triggered = Arc::new(AtomicBool::new(false));
let name_changed_clone = name_changed_triggered.clone();
obj.connect("name-changed", false, move |args| {
let _obj = args[0].get::<Object>().expect("Failed to get args[0]");
let name = args[1].get::<&str>().expect("Failed to get args[1]");
assert_eq!(name, "new-name");
name_changed_clone.store(true, Ordering::Relaxed);
None
});
assert_eq!(obj.property::<String>("name"), String::from("old-name"));
assert!(!name_changed_triggered.load(Ordering::Relaxed));
assert_eq!(
obj.emit_by_name::<String>("change-name", &[&"new-name"]),
"old-name"
);
assert!(name_changed_triggered.load(Ordering::Relaxed));
}
#[test]
fn test_signal_return_expected_type() {
let obj = Object::with_type(SimpleObject::static_type());
obj.connect("create-string", false, move |_args| {
Some("return value".to_value())
});
let signal_id = imp::SimpleObject::signals()[2].signal_id();
let value = obj.emit::<String>(signal_id, &[]);
assert_eq!(value, "return value");
}
#[test]
fn test_callback_validity() {
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
let obj = Object::builder::<SimpleObject>()
.property("name", "old-name")
.build();
let name_changed_triggered = Arc::new(AtomicBool::new(false));
let name_changed_clone = name_changed_triggered.clone();
obj.connect_notify(Some("name"), move |_, _| {
name_changed_clone.store(true, Ordering::Relaxed);
});
obj.notify("name");
assert!(name_changed_triggered.load(Ordering::Relaxed));
}
// Note: can't test type mismatch in signals since panics accross FFI boundaries
// are UB. See https://github.com/gtk-rs/glib/issues/518
#[test]
fn test_signal_return_expected_object_type() {
let obj = Object::with_type(SimpleObject::static_type());
obj.connect("create-child-object", false, move |_args| {
Some(Object::with_type(ChildObject::static_type()).to_value())
});
let value: glib::Object = obj.emit_by_name("create-child-object", &[]);
assert!(value.type_().is_a(ChildObject::static_type()));
}
}
@@ -0,0 +1,163 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{cmp, fmt, hash};
use super::prelude::*;
use crate::{
clone::{Downgrade, Upgrade},
prelude::*,
WeakRef,
};
// rustdoc-stripper-ignore-next
/// Reference-counted wrapper around an [`ObjectSubclass`] reference.
///
/// This can be used for passing into closures as strong or weak reference without manually going
/// from the implementation type to the instance type and back.
pub struct ObjectImplRef<T: ObjectSubclass>(T::Type);
unsafe impl<T: ObjectSubclass + Send + Sync> Send for ObjectImplRef<T> {}
unsafe impl<T: ObjectSubclass + Send + Sync> Sync for ObjectImplRef<T> {}
impl<T: ObjectSubclass> ObjectImplRef<T> {
// rustdoc-stripper-ignore-next
/// Create a new reference-counting wrapper around `imp`.
#[inline]
pub fn new(imp: &T) -> Self {
Self(imp.obj().clone())
}
// rustdoc-stripper-ignore-next
/// Downgrade to a weak reference.
///
/// This can be upgraded to a strong reference again via [`ObjectImplWeakRef::upgrade`].
#[inline]
pub fn downgrade(&self) -> ObjectImplWeakRef<T> {
ObjectImplWeakRef(self.0.downgrade())
}
}
impl<T: ObjectSubclass> Clone for ObjectImplRef<T> {
#[inline]
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: ObjectSubclass> fmt::Debug for ObjectImplRef<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<T::Type as fmt::Debug>::fmt(&self.0, f)
}
}
impl<T: ObjectSubclass> std::ops::Deref for ObjectImplRef<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
T::from_obj(&self.0)
}
}
impl<T: ObjectSubclass> Downgrade for ObjectImplRef<T> {
type Weak = ObjectImplWeakRef<T>;
#[inline]
fn downgrade(&self) -> Self::Weak {
self.downgrade()
}
}
impl<T: ObjectSubclass> PartialOrd for ObjectImplRef<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T: ObjectSubclass, OT: crate::object::ObjectType> PartialOrd<OT> for ObjectImplRef<T>
where
T::Type: PartialOrd<OT>,
{
#[inline]
fn partial_cmp(&self, other: &OT) -> Option<cmp::Ordering> {
self.0.partial_cmp(other)
}
}
impl<T: ObjectSubclass> Ord for ObjectImplRef<T> {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl<T: ObjectSubclass> PartialEq for ObjectImplRef<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T: ObjectSubclass, OT: crate::object::ObjectType> PartialEq<OT> for ObjectImplRef<T>
where
T::Type: PartialEq<OT>,
{
#[inline]
fn eq(&self, other: &OT) -> bool {
self.0 == *other
}
}
impl<T: ObjectSubclass> Eq for ObjectImplRef<T> {}
impl<T: ObjectSubclass> hash::Hash for ObjectImplRef<T> {
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher,
{
self.0.hash(state)
}
}
// rustdoc-stripper-ignore-next
/// Weak reference to an [`ObjectSubclass`] reference.
pub struct ObjectImplWeakRef<T: ObjectSubclass>(WeakRef<T::Type>);
unsafe impl<T: ObjectSubclass + Send + Sync> Send for ObjectImplWeakRef<T> {}
unsafe impl<T: ObjectSubclass + Send + Sync> Sync for ObjectImplWeakRef<T> {}
impl<T: ObjectSubclass> ObjectImplWeakRef<T> {
// rustdoc-stripper-ignore-next
/// Upgrade to a strong reference, if possible.
///
/// This will return `None` if the underlying object was freed in the meantime.
#[inline]
pub fn upgrade(&self) -> Option<ObjectImplRef<T>> {
let obj = self.0.upgrade()?;
Some(ObjectImplRef(obj))
}
}
impl<T: ObjectSubclass> Clone for ObjectImplWeakRef<T> {
#[inline]
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: ObjectSubclass> fmt::Debug for ObjectImplWeakRef<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<crate::WeakRef<T::Type> as fmt::Debug>::fmt(&self.0, f)
}
}
impl<T: ObjectSubclass> Upgrade for ObjectImplWeakRef<T> {
type Strong = ObjectImplRef<T>;
#[inline]
fn upgrade(&self) -> Option<Self::Strong> {
self.upgrade()
}
}
@@ -0,0 +1,295 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! Module for registering shared types for Rust types.
use crate::{prelude::*, translate::*};
pub unsafe trait RefCounted: Clone + Sized + 'static {
// rustdoc-stripper-ignore-next
/// The inner type
type InnerType;
// rustdoc-stripper-ignore-next
/// The function used to increment the inner type refcount
unsafe fn ref_(this: *const Self::InnerType) -> *const Self::InnerType;
// rustdoc-stripper-ignore-next
/// Provides access to a raw pointer to InnerType
fn as_ptr(&self) -> *const Self::InnerType;
// rustdoc-stripper-ignore-next
/// Converts the RefCounted object to a raw pointer to InnerType
unsafe fn into_raw(self) -> *const Self::InnerType;
// rustdoc-stripper-ignore-next
/// Converts a raw pointer to InnerType to a RefCounted object
unsafe fn from_raw(this: *const Self::InnerType) -> Self;
}
unsafe impl<T> RefCounted for std::sync::Arc<T>
where
T: 'static,
{
type InnerType = T;
#[inline]
unsafe fn ref_(this: *const Self::InnerType) -> *const Self::InnerType {
std::sync::Arc::increment_strong_count(this);
this
}
#[inline]
fn as_ptr(&self) -> *const Self::InnerType {
std::sync::Arc::as_ptr(self)
}
#[inline]
unsafe fn into_raw(self) -> *const Self::InnerType {
std::sync::Arc::into_raw(self)
}
#[inline]
unsafe fn from_raw(this: *const Self::InnerType) -> Self {
std::sync::Arc::from_raw(this)
}
}
unsafe impl<T> RefCounted for std::rc::Rc<T>
where
T: 'static,
{
type InnerType = T;
#[inline]
unsafe fn ref_(this: *const Self::InnerType) -> *const Self::InnerType {
use std::mem::ManuallyDrop;
let this_rc = ManuallyDrop::new(std::rc::Rc::from_raw(this));
std::rc::Rc::into_raw(ManuallyDrop::take(&mut this_rc.clone()))
}
#[inline]
fn as_ptr(&self) -> *const Self::InnerType {
std::rc::Rc::as_ptr(self)
}
#[inline]
unsafe fn into_raw(self) -> *const Self::InnerType {
std::rc::Rc::into_raw(self)
}
#[inline]
unsafe fn from_raw(this: *const Self::InnerType) -> Self {
std::rc::Rc::from_raw(this)
}
}
// rustdoc-stripper-ignore-next
/// Trait for defining shared types.
///
/// Links together the type name with the type itself.
///
/// See [`register_shared_type`] for registering an implementation of this trait
/// with the type system.
///
/// [`register_shared_type`]: fn.register_shared_type.html
pub trait SharedType: StaticType + Clone + Sized + 'static {
// rustdoc-stripper-ignore-next
/// Shared type name.
///
/// This must be unique in the whole process.
const NAME: &'static str;
// rustdoc-stripper-ignore-next
/// The inner refcounted type
type RefCountedType: RefCounted;
// rustdoc-stripper-ignore-next
/// Converts the SharedType into its inner RefCountedType
fn into_refcounted(self) -> Self::RefCountedType;
// rustdoc-stripper-ignore-next
/// Constructs a SharedType from a RefCountedType
fn from_refcounted(this: Self::RefCountedType) -> Self;
}
// rustdoc-stripper-ignore-next
/// Register a boxed `glib::Type` ID for `T`.
///
/// This must be called only once and will panic on a second call.
///
/// See [`Shared!`] for defining a function that ensures that
/// this is only called once and returns the type id.
///
/// [`Shared!`]: ../../derive.Shared.html
pub fn register_shared_type<T: SharedType>() -> crate::Type {
unsafe {
use std::ffi::CString;
unsafe extern "C" fn shared_ref<T: SharedType>(v: ffi::gpointer) -> ffi::gpointer {
T::RefCountedType::ref_(v as *const <T::RefCountedType as RefCounted>::InnerType)
as ffi::gpointer
}
unsafe extern "C" fn shared_unref<T: SharedType>(v: ffi::gpointer) {
let _ = T::RefCountedType::from_raw(
v as *const <T::RefCountedType as RefCounted>::InnerType,
);
}
let type_name = CString::new(T::NAME).unwrap();
assert_eq!(
gobject_ffi::g_type_from_name(type_name.as_ptr()),
gobject_ffi::G_TYPE_INVALID,
"Type {} has already been registered",
type_name.to_str().unwrap()
);
let type_ = crate::Type::from_glib(gobject_ffi::g_boxed_type_register_static(
type_name.as_ptr(),
Some(shared_ref::<T>),
Some(shared_unref::<T>),
));
assert!(type_.is_valid());
type_
}
}
#[cfg(test)]
mod test {
use super::*;
// We rename the current crate as glib, since the macros in glib-macros
// generate the glib namespace through the crate_ident_new utility,
// and that returns `glib` (and not `crate`) when called inside the glib crate
use crate as glib;
#[derive(Clone, Debug, PartialEq, Eq)]
struct MySharedInner {
foo: String,
}
#[derive(Clone, Debug, PartialEq, Eq, glib::SharedBoxed)]
#[shared_boxed_type(name = "MySharedArc")]
struct MySharedArc(std::sync::Arc<MySharedInner>);
#[derive(Clone, Debug, PartialEq, Eq, glib::SharedBoxed)]
#[shared_boxed_type(name = "MySharedRc")]
struct MySharedRc(std::rc::Rc<MySharedInner>);
#[test]
fn test_register() {
assert_ne!(crate::Type::INVALID, MySharedArc::static_type());
assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
}
#[test]
fn test_value_arc() {
assert_ne!(crate::Type::INVALID, MySharedArc::static_type());
let b = MySharedArc::from_refcounted(std::sync::Arc::new(MySharedInner {
foo: String::from("abc"),
}));
let v = b.to_value();
let b2 = v.get::<MySharedArc>().unwrap();
assert!(std::sync::Arc::ptr_eq(&b.0, &b2.0));
}
#[test]
fn test_value_rc() {
assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
let b = MySharedRc::from_refcounted(std::rc::Rc::new(MySharedInner {
foo: String::from("abc"),
}));
let v = b.to_value();
let b2 = v.get::<MySharedRc>().unwrap();
assert!(std::rc::Rc::ptr_eq(&b.0, &b2.0));
}
#[test]
fn same_ffi_pointer_arc() {
assert_ne!(crate::Type::INVALID, MySharedArc::static_type());
let b = MySharedArc::from_refcounted(std::sync::Arc::new(MySharedInner {
foo: String::from("abc"),
}));
let inner_raw_ptr = std::sync::Arc::into_raw(b.clone().0);
assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
let inner_raw_ptr_clone =
unsafe { <MySharedArc as SharedType>::RefCountedType::ref_(inner_raw_ptr) };
assert_eq!(std::sync::Arc::strong_count(&b.0), 3);
assert!(std::ptr::eq(inner_raw_ptr, inner_raw_ptr_clone));
let _ = unsafe { <MySharedArc as SharedType>::RefCountedType::from_raw(inner_raw_ptr) };
let _ =
unsafe { <MySharedArc as SharedType>::RefCountedType::from_raw(inner_raw_ptr_clone) };
assert_eq!(std::sync::Arc::strong_count(&b.0), 1);
}
#[test]
fn same_ffi_pointer_rc() {
assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
let b = MySharedRc::from_refcounted(std::rc::Rc::new(MySharedInner {
foo: String::from("abc"),
}));
let inner_raw_ptr = std::rc::Rc::into_raw(b.clone().0);
assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
let inner_raw_ptr_clone =
unsafe { <MySharedRc as SharedType>::RefCountedType::ref_(inner_raw_ptr) };
assert_eq!(std::rc::Rc::strong_count(&b.0), 3);
assert!(std::ptr::eq(inner_raw_ptr, inner_raw_ptr_clone));
let _ = unsafe { <MySharedRc as SharedType>::RefCountedType::from_raw(inner_raw_ptr) };
let _ =
unsafe { <MySharedRc as SharedType>::RefCountedType::from_raw(inner_raw_ptr_clone) };
assert_eq!(std::rc::Rc::strong_count(&b.0), 1);
}
#[test]
fn from_glib_borrow_arc() {
assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
let b = MySharedArc::from_refcounted(std::sync::Arc::new(MySharedInner {
foo: String::from("abc"),
}));
let inner_raw_ptr = std::sync::Arc::into_raw(b.clone().0);
assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
unsafe {
let _ = MySharedArc::from_glib_borrow(inner_raw_ptr);
assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
}
assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
}
#[test]
fn from_glib_borrow_rc() {
assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
let b = MySharedRc::from_refcounted(std::rc::Rc::new(MySharedInner {
foo: String::from("abc"),
}));
let inner_raw_ptr = std::rc::Rc::into_raw(b.clone().0);
assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
unsafe {
let _ = MySharedRc::from_glib_borrow(inner_raw_ptr);
assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
}
assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
}
}
@@ -0,0 +1,715 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{fmt, num::NonZeroU32, ptr, sync::Mutex};
use crate::{
prelude::*, translate::*, utils::is_canonical_pspec_name, Closure, IntoGStr, SignalFlags, Type,
Value,
};
// rustdoc-stripper-ignore-next
/// Builder for signals.
#[allow(clippy::type_complexity)]
#[must_use = "The builder must be built to be used"]
pub struct SignalBuilder {
name: String,
flags: SignalFlags,
param_types: Vec<SignalType>,
return_type: SignalType,
class_handler: Option<
Box<dyn Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static>,
>,
accumulator: Option<
Box<dyn Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static>,
>,
}
// rustdoc-stripper-ignore-next
/// Signal metadata.
pub struct Signal {
name: String,
flags: SignalFlags,
param_types: Vec<SignalType>,
return_type: SignalType,
registration: Mutex<SignalRegistration>,
}
// rustdoc-stripper-ignore-next
/// Token passed to signal class handlers.
pub struct SignalClassHandlerToken(
// rustdoc-stripper-ignore-next
/// Instance for which the signal is emitted.
pub(super) *mut gobject_ffi::GTypeInstance,
// rustdoc-stripper-ignore-next
/// Return type.
pub(super) Type,
// rustdoc-stripper-ignore-next
/// Arguments value array.
pub(super) *const Value,
);
impl fmt::Debug for SignalClassHandlerToken {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.debug_struct("SignalClassHandlerToken")
.field("type", &unsafe {
crate::Object::from_glib_borrow(self.0 as *mut gobject_ffi::GObject)
})
.finish()
}
}
// rustdoc-stripper-ignore-next
/// Signal invocation hint passed to signal accumulators.
#[repr(transparent)]
pub struct SignalInvocationHint(gobject_ffi::GSignalInvocationHint);
impl SignalInvocationHint {
#[inline]
pub fn detail(&self) -> Option<crate::Quark> {
unsafe { try_from_glib(self.0.detail).ok() }
}
#[inline]
pub fn run_type(&self) -> SignalFlags {
unsafe { from_glib(self.0.run_type) }
}
}
impl fmt::Debug for SignalInvocationHint {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.debug_struct("SignalInvocationHint")
.field("detail", &self.detail())
.field("run_type", &self.run_type())
.finish()
}
}
// rustdoc-stripper-ignore-next
/// In-depth information of a specific signal
pub struct SignalQuery(gobject_ffi::GSignalQuery);
unsafe impl Send for SignalQuery {}
unsafe impl Sync for SignalQuery {}
impl SignalQuery {
// rustdoc-stripper-ignore-next
/// The name of the signal.
#[inline]
pub fn signal_name<'a>(&self) -> &'a str {
unsafe {
let ptr = self.0.signal_name;
std::ffi::CStr::from_ptr(ptr).to_str().unwrap()
}
}
// rustdoc-stripper-ignore-next
/// The ID of the signal.
#[inline]
pub fn signal_id(&self) -> SignalId {
unsafe { SignalId::from_glib(self.0.signal_id) }
}
// rustdoc-stripper-ignore-next
/// The instance type this signal can be emitted for.
#[inline]
pub fn type_(&self) -> Type {
unsafe { from_glib(self.0.itype) }
}
// rustdoc-stripper-ignore-next
/// The signal flags.
#[inline]
pub fn flags(&self) -> SignalFlags {
unsafe { from_glib(self.0.signal_flags) }
}
// rustdoc-stripper-ignore-next
/// The return type for the user callback.
#[inline]
pub fn return_type(&self) -> SignalType {
unsafe { from_glib(self.0.return_type) }
}
// rustdoc-stripper-ignore-next
/// The number of parameters the user callback takes.
#[inline]
pub fn n_params(&self) -> u32 {
self.0.n_params
}
// rustdoc-stripper-ignore-next
/// The parameters for the user callback.
#[inline]
pub fn param_types(&self) -> &[SignalType] {
if self.n_params() == 0 {
return &[];
}
unsafe {
std::slice::from_raw_parts(
self.0.param_types as *const SignalType,
self.0.n_params as usize,
)
}
}
}
impl fmt::Debug for SignalQuery {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.debug_struct("SignalQuery")
.field("signal_name", &self.signal_name())
.field("type", &self.type_())
.field("flags", &self.flags())
.field("return_type", &self.return_type())
.field("param_types", &self.param_types())
.finish()
}
}
// rustdoc-stripper-ignore-next
/// Signal ID.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SignalId(NonZeroU32);
impl SignalId {
// rustdoc-stripper-ignore-next
/// Create a new Signal Identifier.
///
/// # Safety
///
/// The caller has to ensure it's a valid signal identifier.
#[inline]
pub unsafe fn new(id: NonZeroU32) -> Self {
Self(id)
}
#[doc(alias = "g_signal_parse_name")]
#[inline]
pub fn parse_name(
name: &str,
type_: Type,
force_detail: bool,
) -> Option<(Self, Option<crate::Quark>)> {
let mut signal_id = std::mem::MaybeUninit::uninit();
let mut detail_quark = std::mem::MaybeUninit::uninit();
unsafe {
let found: bool = name.run_with_gstr(|name| {
from_glib(gobject_ffi::g_signal_parse_name(
name.as_ptr(),
type_.into_glib(),
signal_id.as_mut_ptr(),
detail_quark.as_mut_ptr(),
force_detail.into_glib(),
))
});
if found {
Some((
from_glib(signal_id.assume_init()),
crate::Quark::try_from_glib(detail_quark.assume_init()).ok(),
))
} else {
None
}
}
}
// rustdoc-stripper-ignore-next
/// Find a SignalId by its `name`, and the `type` it connects to.
#[doc(alias = "g_signal_lookup")]
#[inline]
pub fn lookup(name: &str, type_: Type) -> Option<Self> {
unsafe {
let signal_id = name.run_with_gstr(|name| {
gobject_ffi::g_signal_lookup(name.as_ptr(), type_.into_glib())
});
if signal_id == 0 {
None
} else {
Some(Self::new(NonZeroU32::new_unchecked(signal_id)))
}
}
}
// rustdoc-stripper-ignore-next
/// Queries more in-depth information about the current signal.
#[doc(alias = "g_signal_query")]
#[inline]
pub fn query(&self) -> SignalQuery {
unsafe {
let mut query_ptr = std::mem::MaybeUninit::uninit();
gobject_ffi::g_signal_query(self.into_glib(), query_ptr.as_mut_ptr());
let query = query_ptr.assume_init();
debug_assert_ne!(query.signal_id, 0);
SignalQuery(query)
}
}
// rustdoc-stripper-ignore-next
/// Find the signal name.
#[doc(alias = "g_signal_name")]
#[inline]
pub fn name<'a>(&self) -> &'a str {
unsafe {
let ptr = gobject_ffi::g_signal_name(self.into_glib());
std::ffi::CStr::from_ptr(ptr).to_str().unwrap()
}
}
}
#[doc(hidden)]
impl FromGlib<u32> for SignalId {
#[inline]
unsafe fn from_glib(signal_id: u32) -> Self {
debug_assert_ne!(signal_id, 0);
Self::new(NonZeroU32::new_unchecked(signal_id))
}
}
#[doc(hidden)]
impl IntoGlib for SignalId {
type GlibType = u32;
#[inline]
fn into_glib(self) -> u32 {
self.0.into()
}
}
#[derive(Copy, Clone, Hash)]
#[repr(transparent)]
pub struct SignalType(ffi::GType);
impl SignalType {
#[inline]
pub fn with_static_scope(type_: Type) -> Self {
Self(type_.into_glib() | gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT)
}
#[inline]
pub fn static_scope(&self) -> bool {
(self.0 & gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT) != 0
}
#[inline]
pub fn type_(&self) -> Type {
(*self).into()
}
}
impl From<Type> for SignalType {
#[inline]
fn from(type_: Type) -> Self {
Self(type_.into_glib())
}
}
impl From<SignalType> for Type {
#[inline]
fn from(type_: SignalType) -> Self {
// Remove the extra-bit used for G_SIGNAL_TYPE_STATIC_SCOPE
let type_ = type_.0 & (!gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT);
unsafe { from_glib(type_) }
}
}
impl PartialEq<Type> for SignalType {
#[inline]
fn eq(&self, other: &Type) -> bool {
let type_: Type = (*self).into();
type_.eq(other)
}
}
impl std::fmt::Debug for SignalType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let type_: Type = (*self).into();
f.debug_struct("SignalType")
.field("name", &type_.name())
.field("static_scope", &self.static_scope())
.finish()
}
}
impl std::fmt::Display for SignalType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let type_: Type = (*self).into();
f.debug_struct("SignalType")
.field("name", &type_.name())
.field("static_scope", &self.static_scope())
.finish()
}
}
#[doc(hidden)]
impl FromGlib<ffi::GType> for SignalType {
#[inline]
unsafe fn from_glib(type_: ffi::GType) -> Self {
Self(type_)
}
}
#[doc(hidden)]
impl IntoGlib for SignalType {
type GlibType = ffi::GType;
#[inline]
fn into_glib(self) -> ffi::GType {
self.0
}
}
#[allow(clippy::type_complexity)]
enum SignalRegistration {
Unregistered {
class_handler: Option<
Box<
dyn Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
>,
>,
accumulator: Option<
Box<dyn Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static>,
>,
},
Registered {
type_: Type,
signal_id: SignalId,
},
}
impl SignalBuilder {
// rustdoc-stripper-ignore-next
/// The signal's parameters.
pub fn param_types(
mut self,
param_types: impl IntoIterator<Item = impl Into<SignalType>>,
) -> Self {
self.param_types = param_types
.into_iter()
.map(|t| t.into())
.collect::<Vec<_>>();
self
}
// rustdoc-stripper-ignore-next
/// The signal's returned value type.
pub fn return_type<T: StaticType>(mut self) -> Self {
self.return_type = T::static_type().into();
self
}
// rustdoc-stripper-ignore-next
/// The signal's returned value type.
pub fn return_type_from(mut self, type_: impl Into<SignalType>) -> Self {
self.return_type = type_.into();
self
}
// rustdoc-stripper-ignore-next
/// Run the signal class handler in the first emission stage.
pub fn run_first(mut self) -> Self {
self.flags |= SignalFlags::RUN_FIRST;
self
}
// rustdoc-stripper-ignore-next
/// Run the signal class handler in the third emission stage.
pub fn run_last(mut self) -> Self {
self.flags |= SignalFlags::RUN_LAST;
self
}
// rustdoc-stripper-ignore-next
/// Run the signal class handler in the last emission stage.
pub fn run_cleanup(mut self) -> Self {
self.flags |= SignalFlags::RUN_CLEANUP;
self
}
// rustdoc-stripper-ignore-next
/// Signals being emitted for an object while currently being in emission for this very object
/// will not be emitted recursively, but instead cause the first emission to be restarted.
pub fn no_recurse(mut self) -> Self {
self.flags |= SignalFlags::NO_RECURSE;
self
}
// rustdoc-stripper-ignore-next
/// This signal supports "::detail" appendices to the signal name upon handler connections and
/// emissions.
pub fn detailed(mut self) -> Self {
self.flags |= SignalFlags::DETAILED;
self
}
// rustdoc-stripper-ignore-next
/// Action signals are signals that may freely be emitted on alive objects from user code.
pub fn action(mut self) -> Self {
self.flags |= SignalFlags::ACTION;
self
}
// rustdoc-stripper-ignore-next
/// No emissions hooks are supported for this signal.
pub fn no_hooks(mut self) -> Self {
self.flags |= SignalFlags::NO_HOOKS;
self
}
// rustdoc-stripper-ignore-next
/// Varargs signal emission will always collect the arguments, even if there are no signal
/// handlers connected.
pub fn must_collect(mut self) -> Self {
self.flags |= SignalFlags::MUST_COLLECT;
self
}
// rustdoc-stripper-ignore-next
/// The signal is deprecated and will be removed in a future version.
pub fn deprecated(mut self) -> Self {
self.flags |= SignalFlags::DEPRECATED;
self
}
// rustdoc-stripper-ignore-next
/// Explicitly set all flags.
///
/// This overrides previously set flags on this builder.
pub fn flags(mut self, flags: SignalFlags) -> Self {
self.flags = flags;
self
}
// rustdoc-stripper-ignore-next
/// Class handler for this signal.
pub fn class_handler<
F: Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
>(
mut self,
func: F,
) -> Self {
self.class_handler = Some(Box::new(func));
self
}
// rustdoc-stripper-ignore-next
/// Accumulator for the return values of the signal.
///
/// This is called if multiple signal handlers are connected to the signal for accumulating the
/// return values into a single value.
pub fn accumulator<
F: Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
>(
mut self,
func: F,
) -> Self {
self.accumulator = Some(Box::new(func));
self
}
// rustdoc-stripper-ignore-next
/// Build the signal.
///
/// This does not register the signal yet, which only happens as part of object type
/// registration.
#[must_use = "Signal returned from the builder must be used for it to be registered"]
pub fn build(self) -> Signal {
let flags = if self.flags
& (SignalFlags::RUN_FIRST | SignalFlags::RUN_LAST | SignalFlags::RUN_CLEANUP)
== SignalFlags::empty()
{
self.flags | SignalFlags::RUN_LAST
} else {
self.flags
};
Signal {
name: self.name,
flags,
param_types: self.param_types.to_vec(),
return_type: self.return_type,
registration: Mutex::new(SignalRegistration::Unregistered {
class_handler: self.class_handler,
accumulator: self.accumulator,
}),
}
}
}
impl Signal {
// rustdoc-stripper-ignore-next
/// Create a new builder for a signal.
pub fn builder(name: &str) -> SignalBuilder {
assert!(
is_canonical_pspec_name(name),
"{name} is not a valid canonical signal name",
);
SignalBuilder {
name: name.to_owned(),
param_types: Vec::default(),
return_type: <()>::static_type().into(),
flags: SignalFlags::empty(),
class_handler: None,
accumulator: None,
}
}
// rustdoc-stripper-ignore-next
/// Name of the signal.
#[inline]
pub fn name(&self) -> &str {
&self.name
}
// rustdoc-stripper-ignore-next
/// Flags of the signal.
#[inline]
pub fn flags(&self) -> SignalFlags {
self.flags
}
// rustdoc-stripper-ignore-next
/// Parameter types of the signal.
#[inline]
pub fn param_types(&self) -> &[SignalType] {
&self.param_types
}
// rustdoc-stripper-ignore-next
/// Return type of the signal.
#[inline]
pub fn return_type(&self) -> SignalType {
self.return_type
}
// rustdoc-stripper-ignore-next
/// Signal ID.
///
/// This will panic if called before the signal was registered.
#[inline]
pub fn signal_id(&self) -> SignalId {
match &*self.registration.lock().unwrap() {
SignalRegistration::Unregistered { .. } => panic!("Signal not registered yet"),
SignalRegistration::Registered { signal_id, .. } => *signal_id,
}
}
// rustdoc-stripper-ignore-next
/// Type this signal was registered for.
///
/// This will panic if called before the signal was registered.
#[inline]
pub fn type_(&self) -> Type {
match &*self.registration.lock().unwrap() {
SignalRegistration::Unregistered { .. } => panic!("Signal not registered yet"),
SignalRegistration::Registered { type_, .. } => *type_,
}
}
pub(super) fn register(&self, type_: Type) {
let mut registration = self.registration.lock().unwrap();
let (class_handler, accumulator) = match &mut *registration {
SignalRegistration::Unregistered {
class_handler,
accumulator,
} => (class_handler.take(), accumulator.take()),
SignalRegistration::Registered { .. } => unreachable!(),
};
let return_type = self.return_type;
let class_handler = class_handler.map(|class_handler| {
Closure::new(move |values| unsafe {
let instance = gobject_ffi::g_value_get_object(values[0].to_glib_none().0);
let res = class_handler(&SignalClassHandlerToken(instance as *mut _, return_type.into(), values.as_ptr()), values);
if return_type == Type::UNIT {
if let Some(ref v) = res {
panic!("Signal has no return value but class handler returned a value of type {}", v.type_());
}
} else {
match res {
None => {
panic!("Signal has a return value but class handler returned none");
}
Some(ref v) => {
assert!(v.type_().is_a(return_type.into()), "Signal has a return type of {} but class handler returned {}", Type::from(return_type), v.type_());
}
}
}
res
})
});
unsafe extern "C" fn accumulator_trampoline(
ihint: *mut gobject_ffi::GSignalInvocationHint,
return_accu: *mut gobject_ffi::GValue,
handler_return: *const gobject_ffi::GValue,
data: ffi::gpointer,
) -> ffi::gboolean {
let accumulator = &*(data as *const (
SignalType,
Box<
dyn Fn(&SignalInvocationHint, &mut Value, &Value) -> bool
+ Send
+ Sync
+ 'static,
>,
));
let return_accu = &mut *(return_accu as *mut Value);
let handler_return = &*(handler_return as *const Value);
let return_type = accumulator.0;
assert!(
handler_return.type_().is_a(return_type.into()),
"Signal has a return type of {} but handler returned {}",
Type::from(return_type),
handler_return.type_()
);
let res = (accumulator.1)(&SignalInvocationHint(*ihint), return_accu, handler_return)
.into_glib();
assert!(
return_accu.type_().is_a(return_type.into()),
"Signal has a return type of {} but accumulator returned {}",
Type::from(return_type),
return_accu.type_()
);
res
}
let (accumulator, accumulator_trampoline) =
if let (Some(accumulator), true) = (accumulator, return_type != Type::UNIT) {
(
Box::into_raw(Box::new((return_type, accumulator))),
Some::<unsafe extern "C" fn(_, _, _, _) -> _>(accumulator_trampoline),
)
} else {
(ptr::null_mut(), None)
};
unsafe {
let signal_id = gobject_ffi::g_signal_newv(
self.name.to_glib_none().0,
type_.into_glib(),
self.flags.into_glib(),
class_handler.to_glib_none().0,
accumulator_trampoline,
accumulator as ffi::gpointer,
None,
return_type.into_glib(),
self.param_types.len() as u32,
self.param_types.as_ptr() as *mut _,
);
*registration = SignalRegistration::Registered {
type_,
signal_id: SignalId::from_glib(signal_id),
};
}
}
}
File diff suppressed because it is too large. Load diff
+114
View File
@@ -0,0 +1,114 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{
mem, ptr,
sync::atomic::{AtomicUsize, Ordering},
};
fn next_thread_id() -> usize {
static COUNTER: AtomicUsize = AtomicUsize::new(0);
COUNTER.fetch_add(1, Ordering::SeqCst)
}
// rustdoc-stripper-ignore-next
/// Returns a unique ID for the current thread.
///
/// Actual thread IDs can be reused by the OS once the old thread finished.
/// This works around ambiguity created by ID reuse by using a separate TLS counter for threads.
pub fn thread_id() -> usize {
thread_local!(static THREAD_ID: usize = next_thread_id());
THREAD_ID.with(|&x| x)
}
// rustdoc-stripper-ignore-next
/// Thread guard that only gives access to the contained value on the thread it was created on.
pub struct ThreadGuard<T> {
thread_id: usize,
value: T,
}
impl<T> ThreadGuard<T> {
// rustdoc-stripper-ignore-next
/// Create a new thread guard around `value`.
///
/// The thread guard ensures that access to the value is only allowed from the thread it was
/// created on, and otherwise panics.
///
/// The thread guard implements the `Send` trait even if the contained value does not.
#[inline]
pub fn new(value: T) -> Self {
Self {
thread_id: thread_id(),
value,
}
}
// rustdoc-stripper-ignore-next
/// Return a reference to the contained value from the thread guard.
///
/// # Panics
///
/// This function panics if called from a different thread than where the thread guard was
/// created.
#[inline]
pub fn get_ref(&self) -> &T {
assert!(
self.thread_id == thread_id(),
"Value accessed from different thread than where it was created"
);
&self.value
}
// rustdoc-stripper-ignore-next
/// Return a mutable reference to the contained value from the thread guard.
///
/// # Panics
///
/// This function panics if called from a different thread than where the thread guard was
/// created.
#[inline]
pub fn get_mut(&mut self) -> &mut T {
assert!(
self.thread_id == thread_id(),
"Value accessed from different thread than where it was created"
);
&mut self.value
}
// rustdoc-stripper-ignore-next
/// Return the contained value from the thread guard.
///
/// # Panics
///
/// This function panics if called from a different thread than where the thread guard was
/// created.
#[inline]
pub fn into_inner(self) -> T {
assert!(
self.thread_id == thread_id(),
"Value accessed from different thread than where it was created"
);
unsafe { ptr::read(&mem::ManuallyDrop::new(self).value) }
}
// rustdoc-stripper-ignore-next
/// Returns `true` if the current thread owns the value, i.e. it can be accessed safely.
#[inline]
pub fn is_owner(&self) -> bool {
self.thread_id == thread_id()
}
}
impl<T> Drop for ThreadGuard<T> {
#[inline]
fn drop(&mut self) {
assert!(
self.thread_id == thread_id(),
"Value dropped on a different thread than where it was created"
);
}
}
unsafe impl<T> Send for ThreadGuard<T> {}
+254
View File
@@ -0,0 +1,254 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{future::Future, panic, ptr};
use futures_channel::oneshot;
use crate::translate::*;
#[derive(Debug)]
#[doc(alias = "GThreadPool")]
pub struct ThreadPool(ptr::NonNull<ffi::GThreadPool>);
unsafe impl Send for ThreadPool {}
unsafe impl Sync for ThreadPool {}
// rustdoc-stripper-ignore-next
/// A handle to a thread running on a [`ThreadPool`].
///
/// Like [`std::thread::JoinHandle`] for a GLib thread. The return value from the task can be
/// retrieved by calling [`ThreadHandle::join`]. Dropping the handle "detaches" the thread,
/// allowing it to complete but discarding the return value.
#[derive(Debug)]
pub struct ThreadHandle<T> {
rx: std::sync::mpsc::Receiver<std::thread::Result<T>>,
}
impl<T> ThreadHandle<T> {
// rustdoc-stripper-ignore-next
/// Waits for the associated thread to finish.
///
/// Blocks until the associated thread returns. Returns `Ok` with the value returned from the
/// thread, or `Err` if the thread panicked. This function will return immediately if the
/// associated thread has already finished.
#[inline]
pub fn join(self) -> std::thread::Result<T> {
self.rx.recv().unwrap()
}
}
impl ThreadPool {
#[doc(alias = "g_thread_pool_new")]
pub fn shared(max_threads: Option<u32>) -> Result<Self, crate::Error> {
unsafe {
let mut err = ptr::null_mut();
let pool = ffi::g_thread_pool_new(
Some(spawn_func),
ptr::null_mut(),
max_threads.map(|v| v as i32).unwrap_or(-1),
ffi::GFALSE,
&mut err,
);
if pool.is_null() {
Err(from_glib_full(err))
} else {
Ok(ThreadPool(ptr::NonNull::new_unchecked(pool)))
}
}
}
#[doc(alias = "g_thread_pool_new")]
pub fn exclusive(max_threads: u32) -> Result<Self, crate::Error> {
unsafe {
let mut err = ptr::null_mut();
let pool = ffi::g_thread_pool_new(
Some(spawn_func),
ptr::null_mut(),
max_threads as i32,
ffi::GTRUE,
&mut err,
);
if pool.is_null() {
Err(from_glib_full(err))
} else {
Ok(ThreadPool(ptr::NonNull::new_unchecked(pool)))
}
}
}
#[doc(alias = "g_thread_pool_push")]
pub fn push<T: Send + 'static, F: FnOnce() -> T + Send + 'static>(
&self,
func: F,
) -> Result<ThreadHandle<T>, crate::Error> {
let (tx, rx) = std::sync::mpsc::sync_channel(1);
unsafe {
let func: Box<dyn FnOnce() + Send + 'static> = Box::new(move || {
let _ = tx.send(panic::catch_unwind(panic::AssertUnwindSafe(func)));
});
let func = Box::new(func);
let mut err = ptr::null_mut();
let func = Box::into_raw(func);
let ret: bool = from_glib(ffi::g_thread_pool_push(
self.0.as_ptr(),
func as *mut _,
&mut err,
));
if ret {
Ok(ThreadHandle { rx })
} else {
let _ = Box::from_raw(func);
Err(from_glib_full(err))
}
}
}
pub fn push_future<T: Send + 'static, F: FnOnce() -> T + Send + 'static>(
&self,
func: F,
) -> Result<impl Future<Output = std::thread::Result<T>> + Send + Sync + 'static, crate::Error>
{
let (sender, receiver) = oneshot::channel();
self.push(move || {
let _ = sender.send(panic::catch_unwind(panic::AssertUnwindSafe(func)));
})?;
Ok(async move { receiver.await.expect("Dropped before executing") })
}
#[doc(alias = "g_thread_pool_set_max_threads")]
pub fn set_max_threads(&self, max_threads: Option<u32>) -> Result<(), crate::Error> {
unsafe {
let mut err = ptr::null_mut();
let ret: bool = from_glib(ffi::g_thread_pool_set_max_threads(
self.0.as_ptr(),
max_threads.map(|v| v as i32).unwrap_or(-1),
&mut err,
));
if ret {
Ok(())
} else {
Err(from_glib_full(err))
}
}
}
#[doc(alias = "g_thread_pool_get_max_threads")]
#[doc(alias = "get_max_threads")]
pub fn max_threads(&self) -> Option<u32> {
unsafe {
let max_threads = ffi::g_thread_pool_get_max_threads(self.0.as_ptr());
if max_threads == -1 {
None
} else {
Some(max_threads as u32)
}
}
}
#[doc(alias = "g_thread_pool_get_num_threads")]
#[doc(alias = "get_num_threads")]
pub fn num_threads(&self) -> u32 {
unsafe { ffi::g_thread_pool_get_num_threads(self.0.as_ptr()) }
}
#[doc(alias = "g_thread_pool_unprocessed")]
#[doc(alias = "get_unprocessed")]
pub fn unprocessed(&self) -> u32 {
unsafe { ffi::g_thread_pool_unprocessed(self.0.as_ptr()) }
}
#[doc(alias = "g_thread_pool_set_max_unused_threads")]
pub fn set_max_unused_threads(max_threads: Option<u32>) {
unsafe {
ffi::g_thread_pool_set_max_unused_threads(max_threads.map(|v| v as i32).unwrap_or(-1))
}
}
#[doc(alias = "g_thread_pool_get_max_unused_threads")]
#[doc(alias = "get_max_unused_threads")]
pub fn max_unused_threads() -> Option<u32> {
unsafe {
let max_unused_threads = ffi::g_thread_pool_get_max_unused_threads();
if max_unused_threads == -1 {
None
} else {
Some(max_unused_threads as u32)
}
}
}
#[doc(alias = "g_thread_pool_get_num_unused_threads")]
#[doc(alias = "get_num_unused_threads")]
pub fn num_unused_threads() -> u32 {
unsafe { ffi::g_thread_pool_get_num_unused_threads() }
}
#[doc(alias = "g_thread_pool_stop_unused_threads")]
pub fn stop_unused_threads() {
unsafe {
ffi::g_thread_pool_stop_unused_threads();
}
}
#[doc(alias = "g_thread_pool_set_max_idle_time")]
pub fn set_max_idle_time(max_idle_time: u32) {
unsafe { ffi::g_thread_pool_set_max_idle_time(max_idle_time) }
}
#[doc(alias = "g_thread_pool_get_max_idle_time")]
#[doc(alias = "get_max_idle_time")]
pub fn max_idle_time() -> u32 {
unsafe { ffi::g_thread_pool_get_max_idle_time() }
}
}
impl Drop for ThreadPool {
#[inline]
fn drop(&mut self) {
unsafe {
ffi::g_thread_pool_free(self.0.as_ptr(), ffi::GFALSE, ffi::GTRUE);
}
}
}
unsafe extern "C" fn spawn_func(func: ffi::gpointer, _data: ffi::gpointer) {
let func: Box<Box<dyn FnOnce()>> = Box::from_raw(func as *mut _);
func()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_push() {
use std::sync::mpsc;
let p = ThreadPool::exclusive(1).unwrap();
let (sender, receiver) = mpsc::channel();
let handle = p
.push(move || {
sender.send(true).unwrap();
123
})
.unwrap();
assert_eq!(handle.join().unwrap(), 123);
assert_eq!(receiver.recv(), Ok(true));
}
#[test]
fn test_push_future() {
let c = crate::MainContext::new();
let p = ThreadPool::shared(None).unwrap();
let fut = p.push_future(|| true).unwrap();
let res = c.block_on(fut);
assert!(res.unwrap());
}
}
+99
View File
@@ -0,0 +1,99 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use crate::translate::*;
// rustdoc-stripper-ignore-next
/// A value representing an interval of time, in microseconds.
#[doc(alias = "GTimeSpan")]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TimeSpan(pub i64);
impl FromGlib<i64> for TimeSpan {
#[inline]
unsafe fn from_glib(v: i64) -> TimeSpan {
TimeSpan(v)
}
}
impl IntoGlib for TimeSpan {
type GlibType = i64;
#[inline]
fn into_glib(self) -> i64 {
self.0
}
}
impl TimeSpan {
// rustdoc-stripper-ignore-next
/// Create a new timespan from microseconds.
pub fn from_microseconds(v: i64) -> TimeSpan {
TimeSpan(v)
}
// rustdoc-stripper-ignore-next
/// Create a new timespan from milliseconds.
pub fn from_milliseconds(v: i64) -> TimeSpan {
TimeSpan(v * ffi::G_TIME_SPAN_MILLISECOND)
}
// rustdoc-stripper-ignore-next
/// Create a new timespan from seconds.
pub fn from_seconds(v: i64) -> TimeSpan {
TimeSpan(v * ffi::G_TIME_SPAN_SECOND)
}
// rustdoc-stripper-ignore-next
/// Create a new timespan from minutes.
pub fn from_minutes(v: i64) -> TimeSpan {
TimeSpan(v * ffi::G_TIME_SPAN_MINUTE)
}
// rustdoc-stripper-ignore-next
/// Create a new timespan from hours.
pub fn from_hours(v: i64) -> TimeSpan {
TimeSpan(v * ffi::G_TIME_SPAN_HOUR)
}
// rustdoc-stripper-ignore-next
/// Create a new timespan from days.
pub fn from_days(v: i64) -> TimeSpan {
TimeSpan(v * ffi::G_TIME_SPAN_DAY)
}
// rustdoc-stripper-ignore-next
/// Return the full number of microseconds in this `TimeSpan`.
pub fn as_microseconds(self) -> i64 {
self.0
}
// rustdoc-stripper-ignore-next
/// Return the full number of milliseconds in this `TimeSpan`.
pub fn as_milliseconds(self) -> i64 {
self.0 / ffi::G_TIME_SPAN_MILLISECOND
}
// rustdoc-stripper-ignore-next
/// Return the full number of seconds in this `TimeSpan`.
pub fn as_seconds(self) -> i64 {
self.0 / ffi::G_TIME_SPAN_SECOND
}
// rustdoc-stripper-ignore-next
/// Return the full number of minutes in this `TimeSpan`.
pub fn as_minutes(self) -> i64 {
self.0 / ffi::G_TIME_SPAN_MINUTE
}
// rustdoc-stripper-ignore-next
/// Return the full number of hours in this `TimeSpan`.
pub fn as_hours(self) -> i64 {
self.0 / ffi::G_TIME_SPAN_HOUR
}
// rustdoc-stripper-ignore-next
/// Return the full number of days in this `TimeSpan`.
pub fn as_days(self) -> i64 {
self.0 / ffi::G_TIME_SPAN_DAY
}
}
+15
View File
@@ -0,0 +1,15 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use crate::translate::*;
use crate::{TimeType, TimeZone};
impl TimeZone {
#[doc(alias = "g_time_zone_adjust_time")]
pub fn adjust_time(&self, type_: TimeType, mut time: i64) -> (i32, i64) {
unsafe {
let res =
ffi::g_time_zone_adjust_time(self.to_glib_none().0, type_.into_glib(), &mut time);
(res, time)
}
}
}
File diff suppressed because it is too large. Load diff
+678
View File
@@ -0,0 +1,678 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// rustdoc-stripper-ignore-next
//! Runtime type information.
use std::{
fmt,
marker::PhantomData,
mem,
num::{NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU32, NonZeroU64, NonZeroU8},
path::{Path, PathBuf},
ptr,
};
use crate::{translate::*, IntoGStr, Slice};
// rustdoc-stripper-ignore-next
/// A GLib or GLib-based library type
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[doc(alias = "GType")]
#[repr(transparent)]
pub struct Type(ffi::GType);
unsafe impl TransparentType for Type {
type GlibType = ffi::GType;
}
impl Type {
// rustdoc-stripper-ignore-next
/// An invalid `Type` used as error return value in some functions
#[doc(alias = "G_TYPE_INVALID")]
pub const INVALID: Self = Self(gobject_ffi::G_TYPE_INVALID);
// rustdoc-stripper-ignore-next
/// The fundamental type corresponding to the unit type `()`
#[doc(alias = "G_TYPE_NONE")]
pub const UNIT: Self = Self(gobject_ffi::G_TYPE_NONE);
// rustdoc-stripper-ignore-next
/// The fundamental type corresponding to `i8`
#[doc(alias = "G_TYPE_CHAR")]
pub const I8: Self = Self(gobject_ffi::G_TYPE_CHAR);
// rustdoc-stripper-ignore-next
/// The fundamental type corresponding to `u8`
#[doc(alias = "G_TYPE_UCHAR")]
pub const U8: Self = Self(gobject_ffi::G_TYPE_UCHAR);
// rustdoc-stripper-ignore-next
/// The fundamental type corresponding to `bool`
#[doc(alias = "G_TYPE_BOOLEAN")]
pub const BOOL: Self = Self(gobject_ffi::G_TYPE_BOOLEAN);
// rustdoc-stripper-ignore-next
/// The fundamental type corresponding to `i32`
#[doc(alias = "G_TYPE_INT")]
pub const I32: Self = Self(gobject_ffi::G_TYPE_INT);
// rustdoc-stripper-ignore-next
/// The fundamental type corresponding to `u32`
#[doc(alias = "G_TYPE_UINT")]
pub const U32: Self = Self(gobject_ffi::G_TYPE_UINT);
// rustdoc-stripper-ignore-next
/// The fundamental type corresponding to C `long`
#[doc(alias = "G_TYPE_LONG")]
pub const I_LONG: Self = Self(gobject_ffi::G_TYPE_LONG);
// rustdoc-stripper-ignore-next
/// The fundamental type corresponding to C `unsigned long`
#[doc(alias = "G_TYPE_ULONG")]
pub const U_LONG: Self = Self(gobject_ffi::G_TYPE_ULONG);
// rustdoc-stripper-ignore-next
/// The fundamental type corresponding to `i64`
#[doc(alias = "G_TYPE_INT64")]
pub const I64: Self = Self(gobject_ffi::G_TYPE_INT64);
// rustdoc-stripper-ignore-next
/// The fundamental type corresponding to `u64`
#[doc(alias = "G_TYPE_UINT64")]
pub const U64: Self = Self(gobject_ffi::G_TYPE_UINT64);
// rustdoc-stripper-ignore-next
/// The fundamental type corresponding to `f32`
#[doc(alias = "G_TYPE_FLOAT")]
pub const F32: Self = Self(gobject_ffi::G_TYPE_FLOAT);
// rustdoc-stripper-ignore-next
/// The fundamental type corresponding to `f64`
#[doc(alias = "G_TYPE_DOUBLE")]
pub const F64: Self = Self(gobject_ffi::G_TYPE_DOUBLE);
// rustdoc-stripper-ignore-next
/// The fundamental type corresponding to `String`
#[doc(alias = "G_TYPE_STRING")]
pub const STRING: Self = Self(gobject_ffi::G_TYPE_STRING);
// rustdoc-stripper-ignore-next
/// The fundamental type corresponding to a pointer
#[doc(alias = "G_TYPE_POINTER")]
pub const POINTER: Self = Self(gobject_ffi::G_TYPE_POINTER);
// rustdoc-stripper-ignore-next
/// The fundamental type of GVariant
#[doc(alias = "G_TYPE_VARIANT")]
pub const VARIANT: Self = Self(gobject_ffi::G_TYPE_VARIANT);
// rustdoc-stripper-ignore-next
/// The fundamental type from which all interfaces are derived
#[doc(alias = "G_TYPE_INTERFACE")]
pub const INTERFACE: Self = Self(gobject_ffi::G_TYPE_INTERFACE);
// rustdoc-stripper-ignore-next
/// The fundamental type from which all enumeration types are derived
#[doc(alias = "G_TYPE_ENUM")]
pub const ENUM: Self = Self(gobject_ffi::G_TYPE_ENUM);
// rustdoc-stripper-ignore-next
/// The fundamental type from which all flags types are derived
#[doc(alias = "G_TYPE_FLAGS")]
pub const FLAGS: Self = Self(gobject_ffi::G_TYPE_FLAGS);
// rustdoc-stripper-ignore-next
/// The fundamental type from which all boxed types are derived
#[doc(alias = "G_TYPE_BOXED")]
pub const BOXED: Self = Self(gobject_ffi::G_TYPE_BOXED);
// rustdoc-stripper-ignore-next
/// The fundamental type from which all `GParamSpec` types are derived
#[doc(alias = "G_TYPE_PARAM")]
pub const PARAM_SPEC: Self = Self(gobject_ffi::G_TYPE_PARAM);
// rustdoc-stripper-ignore-next
/// The fundamental type from which all objects are derived
#[doc(alias = "G_TYPE_OBJECT")]
pub const OBJECT: Self = Self(gobject_ffi::G_TYPE_OBJECT);
#[doc(alias = "g_type_name")]
pub fn name<'a>(self) -> &'a str {
match self.into_glib() {
gobject_ffi::G_TYPE_INVALID => "<invalid>",
x => unsafe {
let ptr = gobject_ffi::g_type_name(x);
std::ffi::CStr::from_ptr(ptr).to_str().unwrap()
},
}
}
#[doc(alias = "g_type_qname")]
pub fn qname(self) -> crate::Quark {
match self.into_glib() {
gobject_ffi::G_TYPE_INVALID => crate::Quark::from_str("<invalid>"),
x => unsafe { from_glib(gobject_ffi::g_type_qname(x)) },
}
}
#[doc(alias = "g_type_is_a")]
#[inline]
pub fn is_a(self, other: Self) -> bool {
unsafe {
from_glib(gobject_ffi::g_type_is_a(
self.into_glib(),
other.into_glib(),
))
}
}
#[doc(alias = "g_type_parent")]
pub fn parent(self) -> Option<Self> {
unsafe {
let parent: Self = from_glib(gobject_ffi::g_type_parent(self.into_glib()));
Some(parent).filter(|t| t.is_valid())
}
}
#[doc(alias = "g_type_children")]
pub fn children(self) -> Slice<Self> {
unsafe {
let mut n_children = 0u32;
let children = gobject_ffi::g_type_children(self.into_glib(), &mut n_children);
Slice::from_glib_full_num(children, n_children as usize)
}
}
#[doc(alias = "g_type_interfaces")]
pub fn interfaces(self) -> Slice<Self> {
unsafe {
let mut n_interfaces = 0u32;
let interfaces = gobject_ffi::g_type_interfaces(self.into_glib(), &mut n_interfaces);
Slice::from_glib_full_num(interfaces, n_interfaces as usize)
}
}
#[doc(alias = "g_type_interface_prerequisites")]
pub fn interface_prerequisites(self) -> Slice<Self> {
unsafe {
match self {
t if !t.is_a(Self::INTERFACE) => Slice::from_glib_full_num(ptr::null_mut(), 0),
_ => {
let mut n_prereqs = 0u32;
let prereqs = gobject_ffi::g_type_interface_prerequisites(
self.into_glib(),
&mut n_prereqs,
);
Slice::from_glib_full_num(prereqs, n_prereqs as usize)
}
}
}
}
#[doc(alias = "g_type_from_name")]
pub fn from_name(name: impl IntoGStr) -> Option<Self> {
unsafe {
let type_ = name.run_with_gstr(|name| {
Self::from_glib(gobject_ffi::g_type_from_name(name.as_ptr()))
});
Some(type_).filter(|t| t.is_valid())
}
}
// rustdoc-stripper-ignore-next
/// Checks that the type is not [`INVALID`](Self::INVALID)
#[inline]
pub fn is_valid(self) -> bool {
self != Self::INVALID
}
}
impl fmt::Debug for Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.name())
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.name())
}
}
// rustdoc-stripper-ignore-next
/// Types that are supported by GLib dynamic typing.
pub trait StaticType {
// rustdoc-stripper-ignore-next
/// Returns the type identifier of `Self`.
fn static_type() -> Type;
}
impl StaticType for Type {
#[doc(alias = "g_gtype_get_type")]
#[inline]
fn static_type() -> Type {
unsafe { from_glib(gobject_ffi::g_gtype_get_type()) }
}
}
pub trait StaticTypeExt {
// rustdoc-stripper-ignore-next
/// Ensures that the type has been registered with the type system.
#[doc(alias = "g_type_ensure")]
fn ensure_type();
}
impl<T: StaticType> StaticTypeExt for T {
#[inline]
fn ensure_type() {
T::static_type();
}
}
#[doc(hidden)]
impl crate::value::ValueType for Type {
type Type = Type;
}
#[doc(hidden)]
unsafe impl<'a> crate::value::FromValue<'a> for Type {
type Checker = crate::value::GenericValueTypeChecker<Self>;
#[inline]
unsafe fn from_value(value: &'a crate::Value) -> Self {
from_glib(gobject_ffi::g_value_get_gtype(value.to_glib_none().0))
}
}
#[doc(hidden)]
impl crate::value::ToValue for Type {
#[inline]
fn to_value(&self) -> crate::Value {
unsafe {
let mut value = crate::Value::from_type_unchecked(Type::static_type());
gobject_ffi::g_value_set_gtype(value.to_glib_none_mut().0, self.into_glib());
value
}
}
#[inline]
fn value_type(&self) -> crate::Type {
Type::static_type()
}
}
#[doc(hidden)]
impl From<Type> for crate::Value {
#[inline]
fn from(t: Type) -> Self {
crate::value::ToValue::to_value(&t)
}
}
impl<'a, T: ?Sized + StaticType> StaticType for &'a T {
#[inline]
fn static_type() -> Type {
T::static_type()
}
}
impl<'a, T: ?Sized + StaticType> StaticType for &'a mut T {
#[inline]
fn static_type() -> Type {
T::static_type()
}
}
macro_rules! builtin {
($name:ty, $val:ident) => {
impl StaticType for $name {
#[inline]
fn static_type() -> Type {
Type::$val
}
}
};
}
// rustdoc-stripper-ignore-next
/// A GLib pointer
///
/// A raw untyped pointer equivalent to [`*mut Pointee`](Pointee).
pub type Pointer = ffi::gpointer;
// rustdoc-stripper-ignore-next
/// The target of a [Pointer]
///
/// # Examples
///
/// ```
/// use glib::prelude::*;
/// use glib::types::{Pointee, Pointer};
/// use std::ptr::NonNull;
///
/// let pointer = NonNull::<Pointee>::dangling();
/// let value = pointer.to_value();
/// assert!(value.is::<Pointer>());
/// assert_eq!(value.get(), Ok(pointer.as_ptr()));
/// ```
pub type Pointee = libc::c_void;
impl StaticType for ptr::NonNull<Pointee> {
#[inline]
fn static_type() -> Type {
Pointer::static_type()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ILong(pub libc::c_long);
impl std::ops::Deref for ILong {
type Target = libc::c_long;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for ILong {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<libc::c_long> for ILong {
#[inline]
fn from(v: libc::c_long) -> ILong {
ILong(v)
}
}
impl From<ILong> for libc::c_long {
#[inline]
fn from(v: ILong) -> libc::c_long {
v.0
}
}
impl PartialEq<libc::c_long> for ILong {
#[inline]
fn eq(&self, other: &libc::c_long) -> bool {
&self.0 == other
}
}
impl PartialEq<ILong> for libc::c_long {
#[inline]
fn eq(&self, other: &ILong) -> bool {
self == &other.0
}
}
impl PartialOrd<libc::c_long> for ILong {
#[inline]
fn partial_cmp(&self, other: &libc::c_long) -> Option<std::cmp::Ordering> {
self.0.partial_cmp(other)
}
}
impl PartialOrd<ILong> for libc::c_long {
#[inline]
fn partial_cmp(&self, other: &ILong) -> Option<std::cmp::Ordering> {
self.partial_cmp(&other.0)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ULong(pub libc::c_ulong);
impl std::ops::Deref for ULong {
type Target = libc::c_ulong;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for ULong {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<libc::c_ulong> for ULong {
#[inline]
fn from(v: libc::c_ulong) -> ULong {
ULong(v)
}
}
impl From<ULong> for libc::c_ulong {
#[inline]
fn from(v: ULong) -> libc::c_ulong {
v.0
}
}
impl PartialEq<libc::c_ulong> for ULong {
#[inline]
fn eq(&self, other: &libc::c_ulong) -> bool {
&self.0 == other
}
}
impl PartialEq<ULong> for libc::c_ulong {
#[inline]
fn eq(&self, other: &ULong) -> bool {
self == &other.0
}
}
impl PartialOrd<libc::c_ulong> for ULong {
#[inline]
fn partial_cmp(&self, other: &libc::c_ulong) -> Option<std::cmp::Ordering> {
self.0.partial_cmp(other)
}
}
impl PartialOrd<ULong> for libc::c_ulong {
#[inline]
fn partial_cmp(&self, other: &ULong) -> Option<std::cmp::Ordering> {
self.partial_cmp(&other.0)
}
}
builtin!(bool, BOOL);
builtin!(i8, I8);
builtin!(NonZeroI8, I8);
builtin!(u8, U8);
builtin!(NonZeroU8, U8);
builtin!(i32, I32);
builtin!(NonZeroI32, I32);
builtin!(u32, U32);
builtin!(NonZeroU32, U32);
builtin!(i64, I64);
builtin!(NonZeroI64, I64);
builtin!(u64, U64);
builtin!(NonZeroU64, U64);
builtin!(ILong, I_LONG);
builtin!(ULong, U_LONG);
builtin!(f32, F32);
builtin!(f64, F64);
builtin!(str, STRING);
builtin!(String, STRING);
builtin!(PathBuf, STRING);
builtin!(Path, STRING);
builtin!(Pointer, POINTER);
impl<'a> StaticType for [&'a str] {
#[inline]
fn static_type() -> Type {
unsafe { from_glib(ffi::g_strv_get_type()) }
}
}
impl StaticType for Vec<String> {
#[inline]
fn static_type() -> Type {
unsafe { from_glib(ffi::g_strv_get_type()) }
}
}
impl StaticType for () {
#[inline]
fn static_type() -> Type {
Type::UNIT
}
}
#[inline]
pub unsafe fn instance_of<C: StaticType>(ptr: ffi::gconstpointer) -> bool {
from_glib(gobject_ffi::g_type_check_instance_is_a(
ptr as *mut _,
<C as StaticType>::static_type().into_glib(),
))
}
impl FromGlib<ffi::GType> for Type {
#[inline]
unsafe fn from_glib(val: ffi::GType) -> Self {
Self(val)
}
}
impl IntoGlib for Type {
type GlibType = ffi::GType;
#[inline]
fn into_glib(self) -> ffi::GType {
self.0
}
}
impl<'a> ToGlibContainerFromSlice<'a, *mut ffi::GType> for Type {
type Storage = PhantomData<&'a [Type]>;
#[inline]
fn to_glib_none_from_slice(t: &'a [Type]) -> (*mut ffi::GType, Self::Storage) {
(t.as_ptr() as *mut ffi::GType, PhantomData)
}
#[inline]
fn to_glib_container_from_slice(t: &'a [Type]) -> (*mut ffi::GType, Self::Storage) {
(Self::to_glib_full_from_slice(t), PhantomData)
}
fn to_glib_full_from_slice(t: &[Type]) -> *mut ffi::GType {
if t.is_empty() {
return ptr::null_mut();
}
unsafe {
let res =
ffi::g_malloc(mem::size_of::<ffi::GType>() * (t.len() + 1)) as *mut ffi::GType;
std::ptr::copy_nonoverlapping(t.as_ptr() as *const ffi::GType, res, t.len());
*res.add(t.len()) = 0;
res
}
}
}
impl FromGlibContainerAsVec<Type, *const ffi::GType> for Type {
unsafe fn from_glib_none_num_as_vec(ptr: *const ffi::GType, num: usize) -> Vec<Self> {
if num == 0 || ptr.is_null() {
return Vec::new();
}
let mut res = Vec::<Self>::with_capacity(num);
let res_ptr = res.as_mut_ptr() as *mut ffi::GType;
std::ptr::copy_nonoverlapping(ptr, res_ptr, num);
res.set_len(num);
res
}
unsafe fn from_glib_container_num_as_vec(_: *const ffi::GType, _: usize) -> Vec<Self> {
// Can't really free a *const
unimplemented!();
}
unsafe fn from_glib_full_num_as_vec(_: *const ffi::GType, _: usize) -> Vec<Self> {
// Can't really free a *const
unimplemented!();
}
}
impl FromGlibContainerAsVec<Type, *mut ffi::GType> for Type {
unsafe fn from_glib_none_num_as_vec(ptr: *mut ffi::GType, num: usize) -> Vec<Self> {
FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr as *const _, num)
}
unsafe fn from_glib_container_num_as_vec(ptr: *mut ffi::GType, num: usize) -> Vec<Self> {
let res = FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, num);
ffi::g_free(ptr as *mut _);
res
}
unsafe fn from_glib_full_num_as_vec(ptr: *mut ffi::GType, num: usize) -> Vec<Self> {
FromGlibContainerAsVec::from_glib_container_num_as_vec(ptr, num)
}
}
#[cfg(test)]
mod tests {
use std::collections::{BTreeSet, HashSet};
use super::*;
use crate::InitiallyUnowned;
#[test]
fn invalid() {
let invalid = Type::INVALID;
assert_eq!(invalid.name(), "<invalid>");
assert_eq!(invalid.qname(), crate::Quark::from_str("<invalid>"));
assert!(invalid.is_a(Type::INVALID));
assert!(!invalid.is_a(Type::STRING));
assert_eq!(invalid.parent(), None);
assert!(invalid.children().is_empty());
assert!(invalid.interfaces().is_empty());
assert!(invalid.interface_prerequisites().is_empty());
assert!(!invalid.is_valid());
dbg!(&invalid);
}
#[test]
fn hash() {
// Get this first so the type is registered
let iu_type = InitiallyUnowned::static_type();
let set = Type::OBJECT
.children()
.iter()
.copied()
.collect::<HashSet<_>>();
assert!(set.contains(&iu_type));
}
#[test]
fn ord() {
// Get this first so the type is registered
let iu_type = InitiallyUnowned::static_type();
assert!(Type::OBJECT < iu_type);
let set = Type::OBJECT
.children()
.iter()
.copied()
.collect::<BTreeSet<_>>();
assert!(set.contains(&iu_type));
}
}
+243
View File
@@ -0,0 +1,243 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::mem::MaybeUninit;
use crate::{
translate::{from_glib, IntoGlib, UnsafeFrom},
UnicodeBreakType, UnicodeScript, UnicodeType,
};
mod sealed {
pub trait Sealed {}
impl Sealed for char {}
}
impl UnsafeFrom<u32> for char {
#[inline]
unsafe fn unsafe_from(t: u32) -> Self {
debug_assert!(
char::try_from(t).is_ok(),
"glib returned an invalid Unicode codepoint"
);
unsafe { char::from_u32_unchecked(t) }
}
}
// rustdoc-stripper-ignore-next
/// The kind of decomposition to perform
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum DecompositionKind {
// rustdoc-stripper-ignore-next
/// Compatibility decomposition
Compatibility,
// rustdoc-stripper-ignore-next
/// Canonical decomposition
Canonical,
}
// rustdoc-stripper-ignore-next
/// The result of a single step of the Unicode canonical decomposition algorithm
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum CharacterDecomposition {
// rustdoc-stripper-ignore-next
/// The character could not be decomposed further
NoDecomposition,
// rustdoc-stripper-ignore-next
// A 'singleton' decomposition, which means the character was replaced by another
Singleton(char),
// rustdoc-stripper-ignore-next
/// The first character may decompose further, but the second cannot
Pair(char, char),
}
// rustdoc-stripper-ignore-next
/// This trait provides access to Unicode character classification and manipulations functions
/// provided by GLib that do not exist in the standard library
#[doc(alias = "g_unichar")]
pub trait Unichar: sealed::Sealed + Copy + Into<u32> + UnsafeFrom<u32> {
#[doc(alias = "g_unichar_type")]
#[doc(alias = "unichar_type")]
#[inline]
fn unicode_type(self) -> UnicodeType {
unsafe { from_glib(ffi::g_unichar_type(self.into())) }
}
#[doc(alias = "g_unichar_break_type")]
#[doc(alias = "unichar_break_type")]
#[inline]
fn break_type(self) -> UnicodeBreakType {
unsafe { from_glib(ffi::g_unichar_break_type(self.into())) }
}
#[doc(alias = "g_unichar_get_script")]
#[doc(alias = "unichar_get_script")]
#[inline]
fn script(self) -> UnicodeScript {
unsafe { from_glib(ffi::g_unichar_get_script(self.into())) }
}
#[doc(alias = "g_unichar_combining_class")]
#[doc(alias = "unichar_combining_class")]
#[inline]
fn combining_class(self) -> u8 {
// UAX #44 § 5.7.4: The character property invariants regarding Canonical_Combining_Class
// guarantee that [...] all values used will be in the range 0..254.
// So this cast is fine
unsafe { ffi::g_unichar_combining_class(self.into()) as u8 }
}
#[doc(alias = "g_unichar_ismark")]
#[doc(alias = "unichar_ismark")]
#[inline]
fn is_mark(self) -> bool {
unsafe { from_glib(ffi::g_unichar_ismark(self.into())) }
}
#[doc(alias = "g_unichar_isgraph")]
#[doc(alias = "unichar_isgraph")]
#[inline]
fn is_graphical(self) -> bool {
unsafe { from_glib(ffi::g_unichar_isgraph(self.into())) }
}
#[doc(alias = "g_unichar_ispunct")]
#[doc(alias = "unichar_ispunct")]
#[inline]
fn is_punctuation(self) -> bool {
unsafe { from_glib(ffi::g_unichar_ispunct(self.into())) }
}
#[doc(alias = "g_unichar_istitle")]
#[doc(alias = "unichar_istitle")]
#[inline]
fn is_titlecase(self) -> bool {
unsafe { from_glib(ffi::g_unichar_istitle(self.into())) }
}
#[doc(alias = "g_unichar_isdefined")]
#[doc(alias = "unichar_isdefined")]
#[inline]
fn is_defined(self) -> bool {
unsafe { from_glib(ffi::g_unichar_isdefined(self.into())) }
}
#[doc(alias = "g_unichar_iswide")]
#[doc(alias = "unichar_iswide")]
#[inline]
fn is_wide(self) -> bool {
unsafe { from_glib(ffi::g_unichar_iswide(self.into())) }
}
#[doc(alias = "g_unichar_iswide_cjk")]
#[doc(alias = "unichar_iswide_cjk")]
#[inline]
fn is_wide_cjk(self) -> bool {
unsafe { from_glib(ffi::g_unichar_iswide_cjk(self.into())) }
}
#[doc(alias = "g_unichar_iszerowidth")]
#[doc(alias = "unichar_iszerowidth")]
#[inline]
fn is_zero_width(self) -> bool {
unsafe { from_glib(ffi::g_unichar_iszerowidth(self.into())) }
}
#[doc(alias = "g_unichar_totitle")]
#[doc(alias = "unichar_totitle")]
#[inline]
fn to_titlecase(self) -> Self {
unsafe { Self::unsafe_from(ffi::g_unichar_totitle(self.into())) }
}
#[doc(alias = "g_unichar_get_mirror_char")]
#[doc(alias = "unichar_get_mirror_char")]
#[inline]
fn mirror_char(self) -> Option<Self> {
// SAFETY: If g_unichar_get_mirror_char returns true, it will initialize `mirrored`
unsafe {
let mut mirrored = MaybeUninit::uninit();
let res = from_glib(ffi::g_unichar_get_mirror_char(
self.into(),
mirrored.as_mut_ptr(),
));
if res {
Some(Self::unsafe_from(mirrored.assume_init()))
} else {
None
}
}
}
#[doc(alias = "g_unichar_fully_decompose")]
#[doc(alias = "unichar_fully_decompose")]
#[inline]
fn fully_decompose(self, decomposition_kind: DecompositionKind) -> Vec<Self> {
let compat = match decomposition_kind {
DecompositionKind::Compatibility => true,
DecompositionKind::Canonical => false,
};
let buffer_len = ffi::G_UNICHAR_MAX_DECOMPOSITION_LENGTH as usize;
// SAFETY: We assume glib only ever writes valid Unicode codepoints in the provided buffer
// and that it does not lie about the
unsafe {
let mut buffer = Vec::<Self>::with_capacity(buffer_len);
let decomposition_length = ffi::g_unichar_fully_decompose(
self.into(),
compat.into_glib(),
buffer.as_mut_ptr().cast(),
buffer_len,
);
debug_assert!(decomposition_length <= buffer_len);
buffer.set_len(decomposition_length);
buffer
}
}
#[doc(alias = "g_unichar_decompose")]
#[doc(alias = "unichar_decompose")]
#[inline]
fn decompose(self) -> CharacterDecomposition {
// SAFETY: `a` and `b` will always be init after the g_unichar_decompose call returns
unsafe {
let mut a = MaybeUninit::uninit();
let mut b = MaybeUninit::uninit();
let res = from_glib(ffi::g_unichar_decompose(
self.into(),
a.as_mut_ptr(),
b.as_mut_ptr(),
));
if res {
let (a, b) = (a.assume_init(), b.assume_init());
if b == 0 {
CharacterDecomposition::Singleton(char::unsafe_from(a))
} else {
CharacterDecomposition::Pair(char::unsafe_from(a), char::unsafe_from(b))
}
} else {
CharacterDecomposition::NoDecomposition
}
}
}
#[doc(alias = "g_unichar_compose")]
#[doc(alias = "unichar_compose")]
#[inline]
fn compose(a: char, b: char) -> Option<Self> {
// SAFETY: If g_unichar_compose returns true, it will initialize `out`
unsafe {
let mut out = MaybeUninit::uninit();
let res = from_glib(ffi::g_unichar_compose(a.into(), b.into(), out.as_mut_ptr()));
if res {
Some(Self::unsafe_from(out.assume_init()))
} else {
None
}
}
}
}
impl Unichar for char {}
+88
View File
@@ -0,0 +1,88 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use crate::translate::*;
// rustdoc-stripper-ignore-next
/// A `CollationKey` allows ordering strings using the linguistically correct rules for the current locale.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct CollationKey(String);
impl<T: AsRef<str>> From<T> for CollationKey {
// rustdoc-stripper-ignore-next
/// Converts a string into a `CollationKey` that can be compared with other
/// collation keys produced by the same function using `std::cmp::Ordering::cmp()`.
#[doc(alias = "g_utf8_collate_key")]
fn from(s: T) -> Self {
let key =
unsafe { from_glib_full(ffi::g_utf8_collate_key(s.as_ref().to_glib_none().0, -1)) };
Self(key)
}
}
// rustdoc-stripper-ignore-next
/// A `FilenameCollationKey` allows ordering file names using the linguistically correct rules for the current locale.
/// Compared to `CollationKey`, filename collation keys take into consideration dots and other characters
/// commonly found in file names.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct FilenameCollationKey(String);
impl<T: AsRef<str>> From<T> for FilenameCollationKey {
// rustdoc-stripper-ignore-next
/// Converts a string into a `FilenameCollationKey` that can be compared with other
/// collation keys produced by the same function using `std::cmp::Ordering::cmp()`.
#[doc(alias = "g_utf8_collate_key_for_filename")]
fn from(s: T) -> Self {
let key = unsafe {
from_glib_full(ffi::g_utf8_collate_key_for_filename(
s.as_ref().to_glib_none().0,
-1,
))
};
Self(key)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn collate() {
let mut unsorted = vec![
String::from("bcd"),
String::from("cde"),
String::from("abc"),
];
let sorted = vec![
String::from("abc"),
String::from("bcd"),
String::from("cde"),
];
unsorted.sort_by(|s1, s2| CollationKey::from(&s1).cmp(&CollationKey::from(&s2)));
assert_eq!(unsorted, sorted);
}
#[test]
fn collate_filenames() {
let mut unsorted = vec![
String::from("bcd.a"),
String::from("cde.b"),
String::from("abc.c"),
];
let sorted = vec![
String::from("abc.c"),
String::from("bcd.a"),
String::from("cde.b"),
];
unsorted.sort_by(|s1, s2| {
FilenameCollationKey::from(&s1).cmp(&FilenameCollationKey::from(&s2))
});
assert_eq!(unsorted, sorted);
}
}
+278
View File
@@ -0,0 +1,278 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{
ffi::{OsStr, OsString},
mem, ptr,
};
use crate::{translate::*, GString, IntoGStr, IntoOptionalGStr};
// rustdoc-stripper-ignore-next
/// Same as [`get_prgname()`].
///
/// [`get_prgname()`]: fn.get_prgname.html
#[doc(alias = "get_program_name")]
#[inline]
pub fn program_name() -> Option<GString> {
prgname()
}
#[doc(alias = "g_get_prgname")]
#[doc(alias = "get_prgname")]
#[inline]
pub fn prgname() -> Option<GString> {
unsafe { from_glib_none(ffi::g_get_prgname()) }
}
// rustdoc-stripper-ignore-next
/// Same as [`set_prgname()`].
///
/// [`set_prgname()`]: fn.set_prgname.html
#[inline]
pub fn set_program_name(name: Option<impl IntoGStr>) {
set_prgname(name)
}
#[doc(alias = "g_set_prgname")]
#[inline]
pub fn set_prgname(name: Option<impl IntoGStr>) {
name.run_with_gstr(|name| unsafe { ffi::g_set_prgname(name.to_glib_none().0) })
}
#[doc(alias = "g_environ_getenv")]
pub fn environ_getenv<K: AsRef<OsStr>>(envp: &[OsString], variable: K) -> Option<OsString> {
unsafe {
from_glib_none(ffi::g_environ_getenv(
envp.to_glib_none().0,
variable.as_ref().to_glib_none().0,
))
}
}
#[doc(alias = "g_mkstemp")]
pub fn mkstemp<P: AsRef<std::path::Path>>(tmpl: P) -> i32 {
unsafe {
// NOTE: This modifies the string in place, which is fine here because
// to_glib_none() will create a temporary, NUL-terminated copy of the string.
ffi::g_mkstemp(tmpl.as_ref().to_glib_none().0)
}
}
#[doc(alias = "g_mkstemp_full")]
pub fn mkstemp_full(tmpl: impl AsRef<std::path::Path>, flags: i32, mode: i32) -> i32 {
unsafe {
// NOTE: This modifies the string in place, which is fine here because
// to_glib_none() will create a temporary, NUL-terminated copy of the string.
ffi::g_mkstemp_full(tmpl.as_ref().to_glib_none().0, flags, mode)
}
}
#[doc(alias = "g_mkdtemp")]
pub fn mkdtemp(tmpl: impl AsRef<std::path::Path>) -> Option<std::path::PathBuf> {
unsafe {
// NOTE: This modifies the string in place and returns it but does not free it
// if it returns NULL.
let tmpl = tmpl.as_ref().to_glib_full();
let res = ffi::g_mkdtemp(tmpl);
if res.is_null() {
ffi::g_free(tmpl as ffi::gpointer);
None
} else {
from_glib_full(res)
}
}
}
#[doc(alias = "g_mkdtemp_full")]
pub fn mkdtemp_full(tmpl: impl AsRef<std::path::Path>, mode: i32) -> Option<std::path::PathBuf> {
unsafe {
// NOTE: This modifies the string in place and returns it but does not free it
// if it returns NULL.
let tmpl = tmpl.as_ref().to_glib_full();
let res = ffi::g_mkdtemp_full(tmpl, mode);
if res.is_null() {
ffi::g_free(tmpl as ffi::gpointer);
None
} else {
from_glib_full(res)
}
}
}
#[doc(alias = "g_file_get_contents")]
pub fn file_get_contents(
filename: impl AsRef<std::path::Path>,
) -> Result<crate::Slice<u8>, crate::Error> {
unsafe {
let mut contents = ptr::null_mut();
let mut length = mem::MaybeUninit::uninit();
let mut error = ptr::null_mut();
let _ = ffi::g_file_get_contents(
filename.as_ref().to_glib_none().0,
&mut contents,
length.as_mut_ptr(),
&mut error,
);
if error.is_null() {
Ok(crate::Slice::from_glib_full_num(
contents,
length.assume_init() as _,
))
} else {
Err(from_glib_full(error))
}
}
}
pub fn is_canonical_pspec_name(name: &str) -> bool {
name.as_bytes().iter().enumerate().all(|(i, c)| {
i != 0 && (*c >= b'0' && *c <= b'9' || *c == b'-')
|| (*c >= b'A' && *c <= b'Z')
|| (*c >= b'a' && *c <= b'z')
})
}
#[doc(alias = "g_uri_escape_string")]
pub fn uri_escape_string(
unescaped: impl IntoGStr,
reserved_chars_allowed: Option<impl IntoGStr>,
allow_utf8: bool,
) -> crate::GString {
unescaped.run_with_gstr(|unescaped| {
reserved_chars_allowed.run_with_gstr(|reserved_chars_allowed| unsafe {
from_glib_full(ffi::g_uri_escape_string(
unescaped.to_glib_none().0,
reserved_chars_allowed.to_glib_none().0,
allow_utf8.into_glib(),
))
})
})
}
#[doc(alias = "g_uri_unescape_string")]
pub fn uri_unescape_string(
escaped_string: impl IntoGStr,
illegal_characters: Option<impl IntoGStr>,
) -> Option<crate::GString> {
escaped_string.run_with_gstr(|escaped_string| {
illegal_characters.run_with_gstr(|illegal_characters| unsafe {
from_glib_full(ffi::g_uri_unescape_string(
escaped_string.to_glib_none().0,
illegal_characters.to_glib_none().0,
))
})
})
}
#[doc(alias = "g_uri_parse_scheme")]
pub fn uri_parse_scheme(uri: impl IntoGStr) -> Option<crate::GString> {
uri.run_with_gstr(|uri| unsafe {
from_glib_full(ffi::g_uri_parse_scheme(uri.to_glib_none().0))
})
}
#[doc(alias = "g_uri_unescape_segment")]
pub fn uri_unescape_segment(
escaped_string: Option<impl IntoGStr>,
escaped_string_end: Option<impl IntoGStr>,
illegal_characters: Option<impl IntoGStr>,
) -> Option<crate::GString> {
escaped_string.run_with_gstr(|escaped_string| {
escaped_string_end.run_with_gstr(|escaped_string_end| {
illegal_characters.run_with_gstr(|illegal_characters| unsafe {
from_glib_full(ffi::g_uri_unescape_segment(
escaped_string.to_glib_none().0,
escaped_string_end.to_glib_none().0,
illegal_characters.to_glib_none().0,
))
})
})
})
}
#[cfg(test)]
mod tests {
use std::{env, sync::Mutex};
//Mutex to prevent run environment tests parallel
static LOCK: once_cell::sync::Lazy<Mutex<()>> = once_cell::sync::Lazy::new(|| Mutex::new(()));
const VAR_NAME: &str = "function_environment_test";
fn check_getenv(val: &str) {
let _data = LOCK.lock().unwrap();
env::set_var(VAR_NAME, val);
assert_eq!(env::var_os(VAR_NAME), Some(val.into()));
assert_eq!(crate::getenv(VAR_NAME), Some(val.into()));
let environ = crate::environ();
assert_eq!(crate::environ_getenv(&environ, VAR_NAME), Some(val.into()));
}
fn check_setenv(val: &str) {
let _data = LOCK.lock().unwrap();
crate::setenv(VAR_NAME, val, true).unwrap();
assert_eq!(env::var_os(VAR_NAME), Some(val.into()));
}
#[test]
fn getenv() {
check_getenv("Test");
check_getenv("Тест"); // "Test" in Russian
}
#[test]
fn setenv() {
check_setenv("Test");
check_setenv("Тест"); // "Test" in Russian
}
#[test]
fn test_filename_from_uri() {
use std::path::PathBuf;
use crate::GString;
let uri: GString = "file:///foo/bar.txt".into();
if let Ok((filename, hostname)) = crate::filename_from_uri(&uri) {
assert_eq!(filename, PathBuf::from(r"/foo/bar.txt"));
assert_eq!(hostname, None);
} else {
unreachable!();
}
let uri: GString = "file://host/foo/bar.txt".into();
if let Ok((filename, hostname)) = crate::filename_from_uri(&uri) {
assert_eq!(filename, PathBuf::from(r"/foo/bar.txt"));
assert_eq!(hostname, Some(GString::from("host")));
} else {
unreachable!();
}
}
#[test]
fn test_uri_parsing() {
use crate::GString;
assert_eq!(
crate::uri_parse_scheme("foo://bar"),
Some(GString::from("foo"))
);
assert_eq!(crate::uri_parse_scheme("foo"), None);
let escaped = crate::uri_escape_string("&foo", crate::NONE_STR, true);
assert_eq!(escaped, GString::from("%26foo"));
let unescaped = crate::uri_unescape_string(escaped.as_str(), crate::GStr::NONE);
assert_eq!(unescaped, Some(GString::from("&foo")));
assert_eq!(
crate::uri_unescape_segment(Some("/foo"), crate::NONE_STR, crate::NONE_STR),
Some(GString::from("/foo"))
);
assert_eq!(
crate::uri_unescape_segment(Some("/foo%"), crate::NONE_STR, crate::NONE_STR),
None
);
}
}
File diff suppressed because it is too large. Load diff
+251
View File
@@ -0,0 +1,251 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{cmp::Ordering, ops, slice};
use crate::{
prelude::*,
translate::*,
value::{
FromValue, GenericValueTypeOrNoneChecker, ToValueOptional, ValueType, ValueTypeOptional,
},
HasParamSpec, ParamSpecValueArray, ParamSpecValueArrayBuilder, Type, Value,
};
wrapper! {
#[derive(Debug)]
#[doc(alias = "GValueArray")]
pub struct ValueArray(Boxed<gobject_ffi::GValueArray>);
match fn {
copy => |ptr| gobject_ffi::g_value_array_copy(mut_override(ptr)),
free => |ptr| gobject_ffi::g_value_array_free(ptr),
}
}
impl ValueArray {
#[doc(alias = "g_value_array_new")]
pub fn new(n_prealloced: u32) -> ValueArray {
unsafe { from_glib_full(gobject_ffi::g_value_array_new(n_prealloced)) }
}
#[doc(alias = "g_value_array_append")]
pub fn append(&mut self, value: &Value) {
let value = value.to_glib_none();
unsafe {
gobject_ffi::g_value_array_append(self.to_glib_none_mut().0, value.0);
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn len(&self) -> usize {
self.inner.n_values as usize
}
#[doc(alias = "get_nth")]
#[doc(alias = "g_value_array_get_nth")]
pub fn nth(&self, index_: u32) -> Option<Value> {
unsafe {
from_glib_none(gobject_ffi::g_value_array_get_nth(
mut_override(self.to_glib_none().0),
index_,
))
}
}
#[doc(alias = "g_value_array_insert")]
pub fn insert(&mut self, index_: u32, value: &Value) {
let value = value.to_glib_none();
unsafe {
gobject_ffi::g_value_array_insert(self.to_glib_none_mut().0, index_, value.0);
}
}
#[doc(alias = "g_value_array_prepend")]
pub fn prepend(&mut self, value: &Value) {
let value = value.to_glib_none();
unsafe {
gobject_ffi::g_value_array_prepend(self.to_glib_none_mut().0, value.0);
}
}
#[doc(alias = "g_value_array_remove")]
pub fn remove(&mut self, index_: u32) {
unsafe {
gobject_ffi::g_value_array_remove(self.to_glib_none_mut().0, index_);
}
}
#[doc(alias = "g_value_array_sort_with_data")]
pub fn sort_with_data<F: FnMut(&Value, &Value) -> Ordering>(&mut self, compare_func: F) {
unsafe extern "C" fn compare_func_trampoline(
a: ffi::gconstpointer,
b: ffi::gconstpointer,
func: ffi::gpointer,
) -> i32 {
let func = func as *mut &mut (dyn FnMut(&Value, &Value) -> Ordering);
let a = &*(a as *const Value);
let b = &*(b as *const Value);
(*func)(a, b).into_glib()
}
unsafe {
let mut func = compare_func;
let func_obj: &mut (dyn FnMut(&Value, &Value) -> Ordering) = &mut func;
let func_ptr =
&func_obj as *const &mut (dyn FnMut(&Value, &Value) -> Ordering) as ffi::gpointer;
gobject_ffi::g_value_array_sort_with_data(
self.to_glib_none_mut().0,
Some(compare_func_trampoline),
func_ptr,
);
}
}
}
impl ops::Deref for ValueArray {
type Target = [Value];
#[inline]
fn deref(&self) -> &[Value] {
if self.is_empty() {
return &[];
}
unsafe {
slice::from_raw_parts(
(*self.as_ptr()).values as *const Value,
(*self.as_ptr()).n_values as usize,
)
}
}
}
impl ops::DerefMut for ValueArray {
#[inline]
fn deref_mut(&mut self) -> &mut [Value] {
if self.is_empty() {
return &mut [];
}
unsafe {
slice::from_raw_parts_mut(
(*self.as_ptr()).values as *mut Value,
(*self.as_ptr()).n_values as usize,
)
}
}
}
// Implementing `Value` traits manually because of a custom ParamSpec
impl StaticType for ValueArray {
#[inline]
fn static_type() -> Type {
unsafe { from_glib(gobject_ffi::g_value_array_get_type()) }
}
}
#[doc(hidden)]
impl ValueType for ValueArray {
type Type = Self;
}
#[doc(hidden)]
impl ValueTypeOptional for ValueArray {}
#[doc(hidden)]
unsafe impl<'a> FromValue<'a> for ValueArray {
type Checker = GenericValueTypeOrNoneChecker<Self>;
#[inline]
unsafe fn from_value(value: &'a Value) -> Self {
let ptr = gobject_ffi::g_value_dup_boxed(value.to_glib_none().0);
debug_assert!(!ptr.is_null());
from_glib_full(ptr as *mut gobject_ffi::GValueArray)
}
}
#[doc(hidden)]
unsafe impl<'a> FromValue<'a> for &'a ValueArray {
type Checker = GenericValueTypeOrNoneChecker<Self>;
#[inline]
unsafe fn from_value(value: &'a Value) -> Self {
debug_assert_eq!(
std::mem::size_of::<Self>(),
std::mem::size_of::<ffi::gpointer>()
);
let value = &*(value as *const Value as *const gobject_ffi::GValue);
debug_assert!(!value.data[0].v_pointer.is_null());
<ValueArray>::from_glib_ptr_borrow(
&value.data[0].v_pointer as *const ffi::gpointer
as *const *const gobject_ffi::GValueArray,
)
}
}
#[doc(hidden)]
impl ToValue for ValueArray {
#[inline]
fn to_value(&self) -> Value {
unsafe {
let mut value = Value::from_type_unchecked(<Self as StaticType>::static_type());
gobject_ffi::g_value_take_boxed(
value.to_glib_none_mut().0,
ToGlibPtr::<*mut gobject_ffi::GValueArray>::to_glib_full(self) as *mut _,
);
value
}
}
#[inline]
fn value_type(&self) -> Type {
<Self as StaticType>::static_type()
}
}
impl std::convert::From<ValueArray> for Value {
#[inline]
fn from(o: ValueArray) -> Self {
unsafe {
let mut value = Value::from_type_unchecked(<ValueArray as StaticType>::static_type());
gobject_ffi::g_value_take_boxed(
value.to_glib_none_mut().0,
IntoGlibPtr::<*mut gobject_ffi::GValueArray>::into_glib_ptr(o) as *mut _,
);
value
}
}
}
#[doc(hidden)]
impl ToValueOptional for ValueArray {
#[inline]
fn to_value_optional(s: Option<&Self>) -> Value {
let mut value = Value::for_value_type::<Self>();
unsafe {
gobject_ffi::g_value_take_boxed(
value.to_glib_none_mut().0,
ToGlibPtr::<*mut gobject_ffi::GValueArray>::to_glib_full(&s) as *mut _,
);
}
value
}
}
impl HasParamSpec for ValueArray {
type ParamSpec = ParamSpecValueArray;
type SetValue = Self;
type BuilderFn = fn(&str) -> ParamSpecValueArrayBuilder;
fn param_spec_builder() -> Self::BuilderFn {
Self::ParamSpec::builder
}
}
File diff suppressed because it is too large. Load diff
+293
View File
@@ -0,0 +1,293 @@
// Take a look at the license at the top of the repository in the LICENSE file.
use std::{borrow::Cow, default::Default};
use crate::{translate::*, variant::*, variant_type::*};
wrapper! {
// rustdoc-stripper-ignore-next
/// `VariantDict` is a mutable key/value store where the keys are always
/// strings and the values are [`Variant`s](variant/struct.Variant.html).
///
/// Variant dictionaries can easily be converted to/from `Variant`s of the
/// appropriate type. In `glib` terms, this is a variant of the form `"a{sv}"`.
///
/// # Panics
///
/// Note, pretty much all methods on this struct will panic if the
/// [`end_unsafe()`](#method.end_unsafe) method was called on the instance.
#[doc(alias = "GVariantDict")]
pub struct VariantDict(Shared<ffi::GVariantDict>);
match fn {
ref => |ptr| ffi::g_variant_dict_ref(ptr),
unref => |ptr| ffi::g_variant_dict_unref(ptr),
type_ => || ffi::g_variant_dict_get_type(),
}
}
impl VariantDict {
// rustdoc-stripper-ignore-next
/// Create a new `VariantDict` optionally populating it with the given `Variant`
///
/// Since `Variant`s are immutable, this does not couple the `VariantDict` with
/// the input `Variant`, instead the contents are copied into the `VariantDict`.
///
/// # Panics
///
/// This function will panic if the given `Variant` is not of the correct type.
#[doc(alias = "g_variant_dict_new")]
pub fn new(from_asv: Option<&Variant>) -> Self {
if let Some(var) = from_asv {
assert_eq!(var.type_(), VariantDict::static_variant_type());
}
unsafe { from_glib_full(ffi::g_variant_dict_new(from_asv.to_glib_none().0)) }
}
// rustdoc-stripper-ignore-next
/// Check if this `VariantDict` contains the given key.
///
/// Look up whether or not the given key is present, returning `true` if it
/// is present in `self`.
#[doc(alias = "g_variant_dict_contains")]
pub fn contains(&self, key: &str) -> bool {
unsafe {
from_glib(ffi::g_variant_dict_contains(
self.to_glib_none().0,
key.to_glib_none().0,
))
}
}
// rustdoc-stripper-ignore-next
/// Look up a typed value from this `VariantDict`.
///
/// The given `key` is looked up in `self`.
///
/// This will return `None` if the `key` is not present in the dictionary,
/// and an error if the key is present but with the wrong type.
#[doc(alias = "g_variant_dict_lookup")]
pub fn lookup<T: FromVariant>(&self, key: &str) -> Result<Option<T>, VariantTypeMismatchError> {
self.lookup_value(key, None)
.map(|v| Variant::try_get(&v))
.transpose()
}
// rustdoc-stripper-ignore-next
/// Look up and return a value from this `VariantDict`.
///
/// The given `key` is looked up in `self`. If `expected_type` is not
/// `None` then it will be matched against the type of any found value.
///
/// This will return `None` if the `key` is not present in the dictionary
/// or if it is present but the type of the value does not match a given
/// `expected_type`. Otherwise, `Some(value)` will be returned where
/// the `value` is an instance of [`Variant`](variant/struct.Variant.html).
#[doc(alias = "g_variant_dict_lookup_value")]
pub fn lookup_value(&self, key: &str, expected_type: Option<&VariantTy>) -> Option<Variant> {
unsafe {
from_glib_full(ffi::g_variant_dict_lookup_value(
self.to_glib_none().0,
key.to_glib_none().0,
expected_type.to_glib_none().0,
))
}
}
// rustdoc-stripper-ignore-next
/// Insert a variant into the dictionary.
///
/// The given `key`/`value` pair is inserted into `self`. If a value
/// was previously associated with `key` then it is overwritten.
///
/// For convenience, you may use the [`insert()`](#method.insert) if
/// you have a value which implements [`ToVariant`](variant/trait.ToVariant.html).
#[doc(alias = "g_variant_dict_insert_value")]
pub fn insert_value(&self, key: &str, value: &Variant) {
unsafe {
ffi::g_variant_dict_insert_value(
self.to_glib_none().0,
key.to_glib_none().0,
value.to_glib_none().0,
)
}
}
// rustdoc-stripper-ignore-next
/// Insert a value into the dictionary
///
/// The given `key`/`value` pair is inserted into `self`. If a value
/// was previously associated with `key` then it is overwritten.
///
/// This is a convenience method which automatically calls
/// [`to_variant()`](variant/trait.ToVariant.html#method.to_variant) for you
/// on the given value.
///
/// If, on the other hand, you have a [`Variant`](variant/struct.Variant.html)
/// instance already, you should use the [`insert_value()`](#method.insert_value)
/// method instead.
#[doc(alias = "g_variant_dict_insert_value")]
pub fn insert(&self, key: &str, value: impl Into<Variant>) {
unsafe {
ffi::g_variant_dict_insert_value(
self.to_glib_none().0,
key.to_glib_none().0,
value.into().to_glib_none().0,
)
}
}
// rustdoc-stripper-ignore-next
/// Remove the given `key` from the dictionary.
///
/// This removes the given `key` from the dictionary, releasing the reference
/// on the associated value if one is present.
///
/// If a `key`/`value` pair was removed from the dictionary, `true` is
/// returned. If `key` was not present then `false` is returned instead.
#[doc(alias = "g_variant_dict_remove")]
pub fn remove(&self, key: &str) -> bool {
unsafe {
from_glib(ffi::g_variant_dict_remove(
self.to_glib_none().0,
key.to_glib_none().0,
))
}
}
// rustdoc-stripper-ignore-next
/// Convert this dictionary to a [`Variant`](variant/struct.Variant.html)
///
/// This method converts `self` into an instance of [`Variant`](variant/struct.Variant.html)
/// but in doing so renders it very unsafe to use.
///
/// # Safety
///
/// After calling this, the underlying `GVariantDict` is in a state where
/// the only valid operations to perform as reference ones. As such
/// any attempt to read/update the dictionary *will* fail and emit warnings
/// of such.
///
/// You should only use this function if the extra cost of the safe function
/// is too much for your performance critical codepaths
pub unsafe fn end_unsafe(&self) -> Variant {
from_glib_none(ffi::g_variant_dict_end(self.to_glib_none().0))
}
// rustdoc-stripper-ignore-next
/// Convert this dictionary to a [`Variant`](variant/struct.Variant.html)
///
/// This method converts `self` into an instance of [`Variant`](variant/struct.Variant.html)
/// and then reinitialises itself in order to be safe for further use.
///
/// If you are certain that nothing other than disposing of references will
/// be done after ending the instance, you can call the
/// [`end_unsafe()`](#method.end_unsafe) method instead to avoid the unnecessary
/// reinitialisation of the dictionary.
pub fn end(&self) -> Variant {
unsafe {
let ret = self.end_unsafe();
// Reinitialise the dict so that we can continue safely
ffi::g_variant_dict_init(self.to_glib_none().0, None::<Variant>.to_glib_none().0);
ret
}
}
}
impl Default for VariantDict {
fn default() -> Self {
Self::new(None)
}
}
impl StaticVariantType for VariantDict {
fn static_variant_type() -> Cow<'static, VariantTy> {
Cow::Borrowed(VariantTy::VARDICT)
}
}
impl ToVariant for VariantDict {
fn to_variant(&self) -> Variant {
self.end()
}
}
impl From<VariantDict> for Variant {
#[inline]
fn from(d: VariantDict) -> Self {
unsafe { d.end_unsafe() }
}
}
impl FromVariant for VariantDict {
fn from_variant(variant: &Variant) -> Option<Self> {
if variant.type_() == VariantDict::static_variant_type() {
Some(Self::new(Some(variant)))
} else {
None
}
}
}
impl From<Variant> for VariantDict {
fn from(other: Variant) -> Self {
Self::new(Some(&other))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn create_destroy() {
let _dict = VariantDict::new(None);
}
#[test]
fn create_roundtrip() {
let dict = VariantDict::default();
let var: Variant = dict.to_variant();
let _dict2: VariantDict = var.into();
}
#[test]
fn create_populate_destroy() {
let dict = VariantDict::default();
dict.insert_value("one", &(1u8.to_variant()));
assert_eq!(dict.lookup_value("one", None), Some(1u8.to_variant()));
}
#[test]
fn create_populate_roundtrip() {
let dict = VariantDict::default();
dict.insert_value("one", &(1u8.to_variant()));
let var: Variant = dict.to_variant();
let dict = VariantDict::from_variant(&var).expect("Not a dict?");
assert_eq!(dict.lookup_value("one", None), Some(1u8.to_variant()));
}
#[test]
fn lookup() -> Result<(), Box<dyn std::error::Error>> {
let dict = VariantDict::default();
dict.insert_value("one", &(1u8.to_variant()));
assert_eq!(dict.lookup::<u8>("one")?.unwrap(), 1u8);
assert_eq!(
dict.lookup::<String>("one").err().unwrap().actual,
u8::static_variant_type()
);
assert!(dict.lookup::<u8>("two")?.is_none());
Ok(())
}
#[test]
fn create_populate_remove() {
let dict = VariantDict::default();
let empty_var = dict.to_variant();
dict.insert("one", 1u64);
assert!(dict.remove("one"));
assert!(!dict.remove("one"));
let var2 = dict.to_variant();
assert_eq!(empty_var, var2);
}
}
+371
View File
@@ -0,0 +1,371 @@
// Take a look at the license at the top of the repository in the LICENSE file.
// This is similar to the GVariantIter provided by glib, but that would
// introduce a heap allocation and doesn't provide a way to determine how
// many items are left in the iterator.
use std::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator};
use crate::{translate::*, Variant};
// rustdoc-stripper-ignore-next
/// Iterator over items in a variant.
#[derive(Debug)]
pub struct VariantIter {
variant: Variant,
head: usize,
tail: usize,
}
impl VariantIter {
pub(crate) fn new(variant: Variant) -> Self {
let tail = variant.n_children();
Self {
variant,
head: 0,
tail,
}
}
}
impl Iterator for VariantIter {
type Item = Variant;
fn next(&mut self) -> Option<Variant> {
if self.head == self.tail {
None
} else {
let value = self.variant.child_value(self.head);
self.head += 1;
Some(value)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.tail - self.head;
(size, Some(size))
}
fn count(self) -> usize {
self.tail - self.head
}
fn nth(&mut self, n: usize) -> Option<Variant> {
let (end, overflow) = self.head.overflowing_add(n);
if end >= self.tail || overflow {
self.head = self.tail;
None
} else {
self.head = end + 1;
Some(self.variant.child_value(end))
}
}
fn last(self) -> Option<Variant> {
if self.head == self.tail {
None
} else {
Some(self.variant.child_value(self.tail - 1))
}
}
}
impl DoubleEndedIterator for VariantIter {
fn next_back(&mut self) -> Option<Variant> {
if self.head == self.tail {
None
} else {
self.tail -= 1;
Some(self.variant.child_value(self.tail))
}
}
fn nth_back(&mut self, n: usize) -> Option<Variant> {
let (end, overflow) = self.tail.overflowing_sub(n);
if end <= self.head || overflow {
self.head = self.tail;
None
} else {
self.tail = end - 1;
Some(self.variant.child_value(end - 1))
}
}
}
impl ExactSizeIterator for VariantIter {}
impl FusedIterator for VariantIter {}
// rustdoc-stripper-ignore-next
/// Iterator over items in a variant of type `as`.
#[derive(Debug)]
pub struct VariantStrIter<'a> {
variant: &'a Variant,
head: usize,
tail: usize,
}
impl<'a> VariantStrIter<'a> {
pub(crate) fn new(variant: &'a Variant) -> Self {
let tail = variant.n_children();
Self {
variant,
head: 0,
tail,
}
}
fn impl_get(&self, i: usize) -> &'a str {
unsafe {
let mut p: *mut libc::c_char = std::ptr::null_mut();
let s = b"&s\0";
ffi::g_variant_get_child(
self.variant.to_glib_none().0,
i,
s as *const u8 as *const _,
&mut p,
std::ptr::null::<i8>(),
);
let p = std::ffi::CStr::from_ptr(p);
p.to_str().unwrap()
}
}
}
impl<'a> Iterator for VariantStrIter<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<&'a str> {
if self.head == self.tail {
None
} else {
let v = self.impl_get(self.head);
self.head += 1;
Some(v)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.tail - self.head;
(size, Some(size))
}
fn count(self) -> usize {
self.tail - self.head
}
fn nth(&mut self, n: usize) -> Option<&'a str> {
let (end, overflow) = self.head.overflowing_add(n);
if end >= self.tail || overflow {
self.head = self.tail;
None
} else {
self.head = end + 1;
Some(self.impl_get(end))
}
}
fn last(self) -> Option<&'a str> {
if self.head == self.tail {
None
} else {
Some(self.impl_get(self.tail - 1))
}
}
}
impl<'a> DoubleEndedIterator for VariantStrIter<'a> {
fn next_back(&mut self) -> Option<&'a str> {
if self.head == self.tail {
None
} else {
self.tail -= 1;
Some(self.impl_get(self.tail))
}
}
fn nth_back(&mut self, n: usize) -> Option<&'a str> {
let (end, overflow) = self.tail.overflowing_sub(n);
if end <= self.head || overflow {
self.head = self.tail;
None
} else {
self.tail = end - 1;
Some(self.impl_get(end - 1))
}
}
}
impl<'a> ExactSizeIterator for VariantStrIter<'a> {}
impl<'a> FusedIterator for VariantStrIter<'a> {}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use crate::{
prelude::*,
variant::{DictEntry, Variant},
};
#[test]
fn test_variant_iter_variant() {
let v = Variant::from_variant(&"foo".to_string().to_variant());
let vec: Vec<String> = v.iter().map(|i| i.get().unwrap()).collect();
assert_eq!(vec, vec!["foo".to_string()]);
}
#[test]
fn test_variant_iter_array() {
let v = Variant::array_from_iter::<String>([
"foo".to_string().to_variant(),
"bar".to_string().to_variant(),
]);
let vec: Vec<String> = v.iter().map(|i| i.get().unwrap()).collect();
let a = vec!["foo".to_string(), "bar".to_string()];
assert_eq!(&vec, &a);
let vec: Vec<_> = v.array_iter_str().unwrap().collect();
assert_eq!(&vec, &a);
}
#[test]
fn test_variant_iter_tuple() {
let v = Variant::tuple_from_iter([
"foo".to_string().to_variant(),
"bar".to_string().to_variant(),
]);
let vec: Vec<String> = v.iter().map(|i| i.get().unwrap()).collect();
assert_eq!(vec, vec!["foo".to_string(), "bar".to_string()]);
}
#[test]
fn test_variant_iter_dictentry() {
let v = DictEntry::new("foo", 1337).to_variant();
println!("{:?}", v.iter().collect::<Vec<_>>());
assert_eq!(v.iter().count(), 2);
}
#[test]
fn test_variant_iter_map() {
let mut map = HashMap::new();
map.insert("foo", 1);
map.insert("bar", 1);
let v = map.to_variant();
assert_eq!(v.iter().count(), 2);
}
#[test]
fn test_variant_iter_nth() {
let v = Variant::array_from_iter::<String>([
"0".to_string().to_variant(),
"1".to_string().to_variant(),
"2".to_string().to_variant(),
"3".to_string().to_variant(),
"4".to_string().to_variant(),
"5".to_string().to_variant(),
]);
let mut iter = v.iter();
assert_eq!(iter.len(), 6);
assert_eq!(
iter.nth(1).map(|v| v.get::<String>().unwrap()),
Some("1".into())
);
assert_eq!(iter.len(), 4);
assert_eq!(
iter.next().map(|v| v.get::<String>().unwrap()),
Some("2".into())
);
assert_eq!(
iter.nth_back(2).map(|v| v.get::<String>().unwrap()),
Some("3".into())
);
assert_eq!(iter.len(), 0);
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
}
#[test]
fn test_variant_iter_count() {
let v = Variant::array_from_iter::<String>([
"0".to_string().to_variant(),
"1".to_string().to_variant(),
"2".to_string().to_variant(),
]);
let iter = v.iter();
assert_eq!(iter.len(), 3);
assert_eq!(iter.count(), 3);
}
#[test]
fn test_variant_iter_last() {
let v = Variant::array_from_iter::<String>([
"0".to_string().to_variant(),
"1".to_string().to_variant(),
"2".to_string().to_variant(),
]);
let iter = v.iter();
assert_eq!(iter.len(), 3);
assert_eq!(
iter.last().map(|v| v.get::<String>().unwrap()),
Some("2".into())
);
}
#[test]
fn test_variant_str_iter_nth() {
let v = Variant::array_from_iter::<String>([
"0".to_string().to_variant(),
"1".to_string().to_variant(),
"2".to_string().to_variant(),
"3".to_string().to_variant(),
"4".to_string().to_variant(),
"5".to_string().to_variant(),
]);
let mut iter = v.array_iter_str().unwrap();
assert_eq!(iter.len(), 6);
assert_eq!(iter.nth(1), Some("1"));
assert_eq!(iter.len(), 4);
assert_eq!(iter.next(), Some("2"));
assert_eq!(iter.nth_back(2), Some("3"));
assert_eq!(iter.len(), 0);
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
}
#[test]
fn test_variant_str_iter_count() {
let v = Variant::array_from_iter::<String>([
"0".to_string().to_variant(),
"1".to_string().to_variant(),
"2".to_string().to_variant(),
]);
let iter = v.array_iter_str().unwrap();
assert_eq!(iter.len(), 3);
assert_eq!(iter.count(), 3);
}
#[test]
fn test_variant_str_iter_last() {
let v = Variant::array_from_iter::<String>([
"0".to_string().to_variant(),
"1".to_string().to_variant(),
"2".to_string().to_variant(),
]);
let iter = v.array_iter_str().unwrap();
assert_eq!(iter.len(), 3);
assert_eq!(iter.last(), Some("2"));
}
}
Loaded 100 of 133 files, more files were not shown because too many files have changed in this diff. Show more