igraph/attributes.rs
1//! Graph, vertex and edge attributes (`igraph_attributes.h`).
2//!
3//! igraph can attach *attributes* to a graph as a whole, to its vertices and
4//! to its edges: a vertex `name`, an edge `weight`, a graph `title`, ... The
5//! C core does not store attributes by itself: it notifies an *attribute
6//! handler* (a table of callbacks, `igraph_attribute_table_t`) of every
7//! structural change so that the handler can keep its data aligned with the
8//! vertex and edge ids. This module plugs igraph's own C attribute handler
9//! (`igraph_cattribute_table`, the one behind the `VAN`/`SETVAN`/... macros of
10//! the C API) into the crate and exposes it with a typed, Rusty API on
11//! [`Graph`].
12//!
13//! # Enabling attributes
14//!
15//! The attribute handler is a **process-wide** setting of the C library (it is
16//! *not* thread-local, even in a thread-safe igraph build, see
17//! `src/graph/attributes.c`). It is turned on with [`enable`]:
18//!
19//! - it is idempotent and irreversible: once enabled, attributes stay enabled
20//! for the rest of the process (detaching a handler would make igraph leak
21//! the attribute storage and let it get out of sync with the graphs);
22//! - it is also called implicitly by every attribute *setter*, so writing an
23//! attribute simply works;
24//! - it must be called *before reading graphs from files* if you want the
25//! foreign readers (GraphML, GML, Pajek, NCOL, LGL, ...) to keep vertex
26//! names, edge weights and the other attributes found in the file, and
27//! before *writing* them if you want the writers to emit your attributes;
28//! - graphs created before [`enable`] carry no attribute storage: they keep
29//! working (this crate installs a *guarded* version of the C handler that
30//! ignores attribute-less graphs instead of crashing, as the raw C handler
31//! would), and their storage is created lazily by the first setter call;
32//! - call it early (e.g. at the start of `main`): the C library stores the
33//! handler in a plain global variable, so enabling it while other threads
34//! are in the middle of igraph calls is best avoided.
35//!
36//! # Semantics of the C attribute handler
37//!
38//! - Attribute values are numbers (`f64`), booleans or strings
39//! ([`AttributeType`]); an attribute has a single type for all the
40//! vertices (or edges) of a graph, fixed when it is first created. Writing a
41//! value of another type is an error.
42//! - Setting a vertex (edge) attribute on a single vertex (edge) creates it
43//! for all the vertices (edges), with the default value (`NaN`, `false` or
44//! `""`) everywhere else. Vertices and edges added later also get the
45//! default value.
46//! - Attributes follow the structure: deleting vertices or edges
47//! ([`Graph::delete_vertices`], [`Graph::delete_edges`]), taking subgraphs
48//! ([`Graph::induced_subgraph`]), permuting vertices
49//! ([`Graph::permute_vertices`]), copying (and [`Clone`]-ing) a graph keep
50//! every value attached to the right vertex or edge.
51//! - Operations that merge vertices or edges decide what to do with the
52//! attributes of the merged elements via an [`AttributeCombination`]. The
53//! plain wrappers [`Graph::simplify`], [`Graph::contract_vertices`] and
54//! [`Graph::to_undirected`] pass *no* combination, so they drop the
55//! attributes of the kind they merge: vertex attributes for
56//! `contract_vertices`; edge attributes for `to_undirected` (except in
57//! [`ToUndirected::Each`] mode) and for `simplify` when it actually merges
58//! multi-edges (when it only deletes loops, or igraph already knows there
59//! are no multi-edges, the remaining edges keep their attributes). Use
60//! their attribute-aware twins
61//! [`Graph::simplify_with_attributes`],
62//! [`Graph::contract_vertices_with_attributes`] and
63//! [`Graph::to_undirected_with_attributes`] to keep them.
64//! - The [`Random`](AttributeCombinationType::Random) combination and the
65//! tie-breaking of boolean majority votes use the default random number
66//! generator of the calling thread: seed it with [`rng::seed`](crate::rng::seed)
67//! for reproducible results.
68//!
69//! # Example
70//!
71//! ```
72//! use igraph::prelude::*;
73//! use igraph::attributes::{self, AttributeKind, AttributeValue};
74//!
75//! attributes::enable().unwrap();
76//! let mut g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false).unwrap();
77//!
78//! g.set_graph_attr_str("title", "triangle").unwrap();
79//! g.set_vertex_attr_str_values("name", &["alice", "bob", "carol"]).unwrap();
80//! g.set_edge_attr_numeric_values("weight", &[1.0, 2.5, 4.0]).unwrap();
81//! g.set_vertex_attr_bool("admin", 0, true).unwrap();
82//!
83//! assert_eq!(g.graph_attr_str("title").unwrap(), "triangle");
84//! assert_eq!(g.vertex_attr_str("name", 1).unwrap(), "bob");
85//! assert_eq!(g.vertex_attr("admin", 2).unwrap(), AttributeValue::Boolean(false));
86//! assert!(g.has_attribute(AttributeKind::Vertex, "name"));
87//!
88//! // Numeric edge attributes are the weight vectors of weighted algorithms.
89//! let w = g.edge_attr_numeric_values("weight", ..).unwrap();
90//! assert_eq!(w, vec![1.0, 2.5, 4.0]);
91//! let strength = g.strength(.., NeighborMode::All, Loops::Twice, Some(&w)).unwrap();
92//! assert_eq!(strength, vec![5.0, 3.5, 6.5]);
93//!
94//! // Attributes follow the structure of the graph.
95//! g.delete_vertices(0).unwrap();
96//! assert_eq!(g.vertex_attr_str_values("name", ..).unwrap(), vec!["bob", "carol"]);
97//! assert_eq!(g.edge_attr_numeric_values("weight", ..).unwrap(), vec![2.5]);
98//! ```
99//!
100//! # Provided functionality
101//!
102//! | Rust | C |
103//! |------|---|
104//! | [`enable`], [`is_enabled`], [`has_attribute_table`] | `igraph_set_attribute_table(&igraph_cattribute_table)`, `igraph_has_attribute_table` |
105//! | [`Graph::graph_attr_numeric`], [`Graph::graph_attr_bool`], [`Graph::graph_attr_str`], [`Graph::graph_attr`] | `igraph_cattribute_GAN`, `GAB`, `GAS` |
106//! | [`Graph::vertex_attr_numeric`], [`Graph::vertex_attr_bool`], [`Graph::vertex_attr_str`], [`Graph::vertex_attr`] | `igraph_cattribute_VAN`, `VAB`, `VAS` |
107//! | [`Graph::edge_attr_numeric`], [`Graph::edge_attr_bool`], [`Graph::edge_attr_str`], [`Graph::edge_attr`] | `igraph_cattribute_EAN`, `EAB`, `EAS` |
108//! | [`Graph::vertex_attr_numeric_values`], [`Graph::vertex_attr_bool_values`], [`Graph::vertex_attr_str_values`], [`Graph::vertex_attr_values`] | `igraph_cattribute_VANV`, `VABV`, `VASV` |
109//! | [`Graph::edge_attr_numeric_values`], [`Graph::edge_attr_bool_values`], [`Graph::edge_attr_str_values`], [`Graph::edge_attr_values`] | `igraph_cattribute_EANV`, `EABV`, `EASV` |
110//! | [`Graph::set_graph_attr_numeric`], [`Graph::set_graph_attr_bool`], [`Graph::set_graph_attr_str`], [`Graph::set_graph_attr`] | `igraph_cattribute_GAN_set`, `GAB_set`, `GAS_set` |
111//! | [`Graph::set_vertex_attr_numeric`], [`Graph::set_vertex_attr_bool`], [`Graph::set_vertex_attr_str`], [`Graph::set_vertex_attr`] | `igraph_cattribute_VAN_set`, `VAB_set`, `VAS_set` |
112//! | [`Graph::set_edge_attr_numeric`], [`Graph::set_edge_attr_bool`], [`Graph::set_edge_attr_str`], [`Graph::set_edge_attr`] | `igraph_cattribute_EAN_set`, `EAB_set`, `EAS_set` |
113//! | [`Graph::set_vertex_attr_numeric_values`], [`Graph::set_vertex_attr_bool_values`], [`Graph::set_vertex_attr_str_values`], [`Graph::set_vertex_attr_values`] | `igraph_cattribute_VAN_setv`, `VAB_setv`, `VAS_setv` |
114//! | [`Graph::set_edge_attr_numeric_values`], [`Graph::set_edge_attr_bool_values`], [`Graph::set_edge_attr_str_values`], [`Graph::set_edge_attr_values`] | `igraph_cattribute_EAN_setv`, `EAB_setv`, `EAS_setv` |
115//! | [`Graph::attribute_list`], [`Graph::attribute_names`], [`Graph::attribute_type`] | `igraph_cattribute_list` |
116//! | [`Graph::has_attribute`] | `igraph_cattribute_has_attr` |
117//! | [`Graph::remove_graph_attr`], [`Graph::remove_vertex_attr`], [`Graph::remove_edge_attr`], [`Graph::remove_all_attributes`] | `igraph_cattribute_remove_g`, `remove_v`, `remove_e`, `remove_all` |
118//! | [`Graph::add_vertices_with_attributes`], [`Graph::add_edges_with_attributes`] | `igraph_add_vertices`, `igraph_add_edges` with an attribute record list |
119//! | [`Graph::simplify_with_attributes`], [`Graph::contract_vertices_with_attributes`], [`Graph::to_undirected_with_attributes`] | `igraph_simplify`, `igraph_contract_vertices`, `igraph_to_undirected` with an attribute combination |
120//! | [`AttributeRecord`] | `igraph_attribute_record_*` |
121//! | [`AttributeCombination`] | `igraph_attribute_combination_*` |
122//!
123//! # See also
124//!
125//! - [`crate::foreign`]: the GraphML, GML, Pajek, NCOL, LGL, ... readers and
126//! writers load and save attributes once [`enable`] has been called, e.g.
127//! [`Graph::read_graph_graphml_from_str`] and
128//! [`Graph::write_graph_graphml_to_string`].
129//! - [`crate::operators`] and [`crate::conversion`]: the structural
130//! operations whose attribute handling is described above.
131//! - Weighted algorithms take the weights as a slice: feed them
132//! `g.edge_attr_numeric_values("weight", ..)`, e.g. [`Graph::strength`],
133//! [`Graph::distances_dijkstra`] or [`Graph::community_multilevel`].
134
135#[cfg(doc)]
136use crate::graph::Graph;
137use crate::{
138 constants::ToUndirected,
139 error::{Error, ErrorKind, Result, ensure_init},
140 ffi::*,
141 graph::{EdgeId, VertexId},
142 igraph_call,
143 selector::{EdgeSelector, VertexSelector},
144 strvector::StrVector,
145 vector::{Vector, VectorBool, VectorInt},
146};
147use std::{
148 ffi::{CStr, CString, c_char},
149 fmt,
150 mem::MaybeUninit,
151 ptr,
152 sync::{
153 Once,
154 atomic::{AtomicBool, Ordering},
155 },
156};
157
158// ---------------------------------------------------------------------------
159// Enumerations
160// ---------------------------------------------------------------------------
161
162crate::ffi_enum! {
163 /// The type of an attribute (`igraph_attribute_type_t`).
164 ///
165 /// The C attribute handler supports [`Numeric`](Self::Numeric),
166 /// [`Boolean`](Self::Boolean) and [`String`](Self::String) attributes;
167 /// the other two variants exist for completeness (they are used by the
168 /// attribute handlers of the high-level igraph interfaces).
169 pub enum AttributeType: igraph_attribute_type_t {
170 /// No type yet (a freshly initialized, empty [`AttributeRecord`]).
171 Unspecified = igraph_attribute_type_t_IGRAPH_ATTRIBUTE_UNSPECIFIED,
172 /// Real numbers (`f64`).
173 Numeric = igraph_attribute_type_t_IGRAPH_ATTRIBUTE_NUMERIC,
174 /// Booleans.
175 Boolean = igraph_attribute_type_t_IGRAPH_ATTRIBUTE_BOOLEAN,
176 /// Strings.
177 String = igraph_attribute_type_t_IGRAPH_ATTRIBUTE_STRING,
178 /// Opaque objects of a high-level language (unsupported by the C handler).
179 Object = igraph_attribute_type_t_IGRAPH_ATTRIBUTE_OBJECT,
180 }
181}
182
183impl fmt::Display for AttributeType {
184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185 f.write_str(match self {
186 Self::Unspecified => "unspecified",
187 Self::Numeric => "numeric",
188 Self::Boolean => "boolean",
189 Self::String => "string",
190 Self::Object => "object",
191 })
192 }
193}
194
195crate::ffi_enum! {
196 /// What an attribute is attached to (`igraph_attribute_elemtype_t`).
197 pub enum AttributeKind: igraph_attribute_elemtype_t {
198 /// The graph as a whole.
199 Graph = igraph_attribute_elemtype_t_IGRAPH_ATTRIBUTE_GRAPH,
200 /// Each vertex.
201 Vertex = igraph_attribute_elemtype_t_IGRAPH_ATTRIBUTE_VERTEX,
202 /// Each edge.
203 Edge = igraph_attribute_elemtype_t_IGRAPH_ATTRIBUTE_EDGE,
204 }
205}
206
207impl fmt::Display for AttributeKind {
208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209 f.write_str(match self {
210 Self::Graph => "graph",
211 Self::Vertex => "vertex",
212 Self::Edge => "edge",
213 })
214 }
215}
216
217crate::ffi_enum! {
218 /// How to combine the attribute values of vertices or edges that are
219 /// merged into one (`igraph_attribute_combination_type_t`).
220 ///
221 /// The C attribute handler supports the following combinations, per
222 /// attribute type (anything else fails with
223 /// [`ErrorKind::AttributeCombination`] or [`ErrorKind::Unimplemented`]):
224 ///
225 /// | combination | numeric | boolean | string |
226 /// |-------------|---------|---------|--------|
227 /// | `Sum` | sum | any is true | – |
228 /// | `Prod` | product | all are true | – |
229 /// | `Min`/`Max` | min/max | all/any are true | – |
230 /// | `Mean` | mean | majority (ties broken at random) | – |
231 /// | `Median` | – (unimplemented) | majority (ties broken at random) | – |
232 /// | `Random`, `First`, `Last` | yes | yes | yes |
233 /// | `Concat` | – | – | concatenation |
234 /// | `Function` | yes | yes | – (not supported by this crate) |
235 ///
236 /// `Ignore` and `Default` drop the attribute for every type.
237 pub enum AttributeCombinationType: igraph_attribute_combination_type_t {
238 /// Drop the attribute from the result.
239 Ignore = igraph_attribute_combination_type_t_IGRAPH_ATTRIBUTE_COMBINE_IGNORE,
240 /// The default behaviour of the handler: for the C handler, the
241 /// attribute is dropped, like [`Ignore`](Self::Ignore).
242 Default = igraph_attribute_combination_type_t_IGRAPH_ATTRIBUTE_COMBINE_DEFAULT,
243 /// A user supplied C function, see [`AttributeCombination::add_function`].
244 Function = igraph_attribute_combination_type_t_IGRAPH_ATTRIBUTE_COMBINE_FUNCTION,
245 /// Sum of the values (logical *or* for booleans).
246 Sum = igraph_attribute_combination_type_t_IGRAPH_ATTRIBUTE_COMBINE_SUM,
247 /// Product of the values (logical *and* for booleans).
248 Prod = igraph_attribute_combination_type_t_IGRAPH_ATTRIBUTE_COMBINE_PROD,
249 /// Minimum of the values (logical *and* for booleans).
250 Min = igraph_attribute_combination_type_t_IGRAPH_ATTRIBUTE_COMBINE_MIN,
251 /// Maximum of the values (logical *or* for booleans).
252 Max = igraph_attribute_combination_type_t_IGRAPH_ATTRIBUTE_COMBINE_MAX,
253 /// A value chosen uniformly at random (uses igraph's RNG).
254 Random = igraph_attribute_combination_type_t_IGRAPH_ATTRIBUTE_COMBINE_RANDOM,
255 /// The value of the first element of the merged group (in the order
256 /// the merging function lists them, usually increasing id).
257 First = igraph_attribute_combination_type_t_IGRAPH_ATTRIBUTE_COMBINE_FIRST,
258 /// The value of the last element of the merged group (usually the
259 /// highest id).
260 Last = igraph_attribute_combination_type_t_IGRAPH_ATTRIBUTE_COMBINE_LAST,
261 /// Arithmetic mean (majority vote for booleans, with ties broken at
262 /// random using igraph's RNG).
263 Mean = igraph_attribute_combination_type_t_IGRAPH_ATTRIBUTE_COMBINE_MEAN,
264 /// Median (majority vote for booleans; not implemented for numbers).
265 Median = igraph_attribute_combination_type_t_IGRAPH_ATTRIBUTE_COMBINE_MEDIAN,
266 /// Concatenation of the strings.
267 ///
268 /// **Note:** in igraph 1.0.0 and 1.0.1 the C handler
269 /// (`igraph_i_cattributes_cs_concat`) concatenates the values of the
270 /// *first* `k` elements of the graph (where `k` is the size of the
271 /// merged group) instead of the values of the merged elements.
272 Concat = igraph_attribute_combination_type_t_IGRAPH_ATTRIBUTE_COMBINE_CONCAT,
273 }
274}
275
276// ---------------------------------------------------------------------------
277// Values
278// ---------------------------------------------------------------------------
279
280/// A single attribute value, as stored by the C attribute handler.
281///
282/// It converts from `f64`, `i32`, `i64`, `bool`, `&str` and `String`, so the
283/// generic setters such as [`Graph::set_vertex_attr`] accept those directly.
284#[derive(Debug, Clone, PartialEq)]
285pub enum AttributeValue {
286 /// A numeric value.
287 Numeric(f64),
288 /// A boolean value.
289 Boolean(bool),
290 /// A string value.
291 String(String),
292}
293
294impl AttributeValue {
295 /// The [`AttributeType`] of this value.
296 pub fn attribute_type(&self) -> AttributeType {
297 match self {
298 Self::Numeric(_) => AttributeType::Numeric,
299 Self::Boolean(_) => AttributeType::Boolean,
300 Self::String(_) => AttributeType::String,
301 }
302 }
303
304 /// The number, if this is a numeric value.
305 pub fn as_f64(&self) -> Option<f64> {
306 match self {
307 Self::Numeric(x) => Some(*x),
308 _ => None,
309 }
310 }
311
312 /// The boolean, if this is a boolean value.
313 pub fn as_bool(&self) -> Option<bool> {
314 match self {
315 Self::Boolean(b) => Some(*b),
316 _ => None,
317 }
318 }
319
320 /// The string, if this is a string value.
321 pub fn as_str(&self) -> Option<&str> {
322 match self {
323 Self::String(s) => Some(s),
324 _ => None,
325 }
326 }
327}
328
329impl fmt::Display for AttributeValue {
330 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331 match self {
332 Self::Numeric(x) => write!(f, "{x}"),
333 Self::Boolean(b) => write!(f, "{b}"),
334 Self::String(s) => write!(f, "{s:?}"),
335 }
336 }
337}
338
339impl From<f64> for AttributeValue {
340 fn from(x: f64) -> Self {
341 Self::Numeric(x)
342 }
343}
344
345impl From<i32> for AttributeValue {
346 fn from(x: i32) -> Self {
347 Self::Numeric(x.into())
348 }
349}
350
351impl From<i64> for AttributeValue {
352 /// Converts to a numeric value (precision is lost beyond 2^53).
353 fn from(x: i64) -> Self {
354 Self::Numeric(x as f64)
355 }
356}
357
358impl From<bool> for AttributeValue {
359 fn from(b: bool) -> Self {
360 Self::Boolean(b)
361 }
362}
363
364impl From<&str> for AttributeValue {
365 fn from(s: &str) -> Self {
366 Self::String(s.to_owned())
367 }
368}
369
370impl From<String> for AttributeValue {
371 fn from(s: String) -> Self {
372 Self::String(s)
373 }
374}
375
376/// The values of an attribute for several vertices or edges.
377///
378/// Converts from `Vec<f64>`, `Vec<bool>`, `Vec<String>`, `Vec<&str>` and the
379/// corresponding slices, so that [`Graph::set_vertex_attr_values`] accepts
380/// them directly.
381#[derive(Debug, Clone, PartialEq)]
382pub enum AttributeValues {
383 /// Numeric values.
384 Numeric(Vec<f64>),
385 /// Boolean values.
386 Boolean(Vec<bool>),
387 /// String values.
388 String(Vec<String>),
389}
390
391impl AttributeValues {
392 /// The [`AttributeType`] of the values.
393 pub fn attribute_type(&self) -> AttributeType {
394 match self {
395 Self::Numeric(_) => AttributeType::Numeric,
396 Self::Boolean(_) => AttributeType::Boolean,
397 Self::String(_) => AttributeType::String,
398 }
399 }
400
401 /// Number of values.
402 pub fn len(&self) -> usize {
403 match self {
404 Self::Numeric(v) => v.len(),
405 Self::Boolean(v) => v.len(),
406 Self::String(v) => v.len(),
407 }
408 }
409
410 /// Whether there are no values.
411 pub fn is_empty(&self) -> bool {
412 self.len() == 0
413 }
414
415 /// The value at `index`, if any.
416 pub fn get(&self, index: usize) -> Option<AttributeValue> {
417 match self {
418 Self::Numeric(v) => v.get(index).copied().map(AttributeValue::Numeric),
419 Self::Boolean(v) => v.get(index).copied().map(AttributeValue::Boolean),
420 Self::String(v) => v.get(index).cloned().map(AttributeValue::String),
421 }
422 }
423
424 /// The numbers, if these are numeric values.
425 pub fn as_numeric(&self) -> Option<&[f64]> {
426 match self {
427 Self::Numeric(v) => Some(v),
428 _ => None,
429 }
430 }
431
432 /// The booleans, if these are boolean values.
433 pub fn as_bool(&self) -> Option<&[bool]> {
434 match self {
435 Self::Boolean(v) => Some(v),
436 _ => None,
437 }
438 }
439
440 /// The strings, if these are string values.
441 pub fn as_strings(&self) -> Option<&[String]> {
442 match self {
443 Self::String(v) => Some(v),
444 _ => None,
445 }
446 }
447}
448
449impl From<Vec<f64>> for AttributeValues {
450 fn from(v: Vec<f64>) -> Self {
451 Self::Numeric(v)
452 }
453}
454
455impl From<&[f64]> for AttributeValues {
456 fn from(v: &[f64]) -> Self {
457 Self::Numeric(v.to_vec())
458 }
459}
460
461impl From<Vec<bool>> for AttributeValues {
462 fn from(v: Vec<bool>) -> Self {
463 Self::Boolean(v)
464 }
465}
466
467impl From<&[bool]> for AttributeValues {
468 fn from(v: &[bool]) -> Self {
469 Self::Boolean(v.to_vec())
470 }
471}
472
473impl From<Vec<String>> for AttributeValues {
474 fn from(v: Vec<String>) -> Self {
475 Self::String(v)
476 }
477}
478
479impl From<Vec<&str>> for AttributeValues {
480 fn from(v: Vec<&str>) -> Self {
481 Self::String(v.into_iter().map(str::to_owned).collect())
482 }
483}
484
485impl From<&[&str]> for AttributeValues {
486 fn from(v: &[&str]) -> Self {
487 Self::String(v.iter().map(|s| (*s).to_owned()).collect())
488 }
489}
490
491/// Names and types of all the attributes of a graph, see [`Graph::attribute_list`].
492#[derive(Debug, Clone, PartialEq, Eq, Default)]
493pub struct AttributeList {
494 /// Graph attributes, in creation order.
495 pub graph: Vec<(String, AttributeType)>,
496 /// Vertex attributes, in creation order.
497 pub vertex: Vec<(String, AttributeType)>,
498 /// Edge attributes, in creation order.
499 pub edge: Vec<(String, AttributeType)>,
500}
501
502impl AttributeList {
503 /// The attributes of the given kind.
504 pub fn of(&self, kind: AttributeKind) -> &[(String, AttributeType)] {
505 match kind {
506 AttributeKind::Graph => &self.graph,
507 AttributeKind::Vertex => &self.vertex,
508 AttributeKind::Edge => &self.edge,
509 }
510 }
511
512 /// Whether the graph has no attributes at all.
513 pub fn is_empty(&self) -> bool {
514 self.graph.is_empty() && self.vertex.is_empty() && self.edge.is_empty()
515 }
516}
517
518// ---------------------------------------------------------------------------
519// The attribute handler
520// ---------------------------------------------------------------------------
521
522/// Fetches a callback of the C attribute handler `igraph_cattribute_table`.
523macro_rules! c_handler {
524 ($field:ident) => {
525 // SAFETY: `igraph_cattribute_table` is an immutable, statically
526 // initialized C constant.
527 unsafe { igraph_cattribute_table.$field }
528 };
529}
530
531/// Reports an error from inside a callback, like `IGRAPH_ERROR` in C.
532fn raise(reason: &'static CStr, code: igraph_error_t) -> igraph_error_t {
533 unsafe {
534 igraph_error(
535 reason.as_ptr(),
536 c"attributes.rs".as_ptr(),
537 line!() as i32,
538 code,
539 )
540 }
541}
542
543fn missing_callback() -> igraph_error_t {
544 raise(
545 c"The C attribute handler lacks a callback.",
546 igraph_error_type_t_IGRAPH_FAILURE,
547 )
548}
549
550fn no_such_attribute() -> igraph_error_t {
551 raise(
552 c"Attribute does not exist (the graph carries no attributes).",
553 igraph_error_type_t_IGRAPH_EINVAL,
554 )
555}
556
557// The guarded handler: igraph's C attribute handler dereferences `graph->attr`
558// unconditionally in most callbacks, which would crash on graphs created
559// before the handler was attached (their `attr` is NULL). The callbacks below
560// delegate to the C handler for graphs that carry attribute storage and treat
561// attribute-less graphs as graphs without attributes.
562
563unsafe extern "C" fn g_init(
564 graph: *mut igraph_t,
565 attr: *const igraph_attribute_record_list_t,
566) -> igraph_error_t {
567 match c_handler!(init) {
568 Some(f) => unsafe { f(graph, attr) },
569 None => missing_callback(),
570 }
571}
572
573unsafe extern "C" fn g_destroy(graph: *mut igraph_t) {
574 if unsafe { !(*graph).attr.is_null() }
575 && let Some(f) = c_handler!(destroy)
576 {
577 unsafe { f(graph) }
578 }
579}
580
581unsafe extern "C" fn g_copy(
582 to: *mut igraph_t,
583 from: *const igraph_t,
584 ga: igraph_bool_t,
585 va: igraph_bool_t,
586 ea: igraph_bool_t,
587) -> igraph_error_t {
588 if unsafe { (*from).attr.is_null() } {
589 return igraph_error_type_t_IGRAPH_SUCCESS;
590 }
591 match c_handler!(copy) {
592 Some(f) => unsafe { f(to, from, ga, va, ea) },
593 None => missing_callback(),
594 }
595}
596
597unsafe extern "C" fn g_add_vertices(
598 graph: *mut igraph_t,
599 nv: igraph_int_t,
600 attr: *const igraph_attribute_record_list_t,
601) -> igraph_error_t {
602 if unsafe { (*graph).attr.is_null() } {
603 return igraph_error_type_t_IGRAPH_SUCCESS;
604 }
605 match c_handler!(add_vertices) {
606 Some(f) => unsafe { f(graph, nv, attr) },
607 None => missing_callback(),
608 }
609}
610
611unsafe extern "C" fn g_add_edges(
612 graph: *mut igraph_t,
613 edges: *const igraph_vector_int_t,
614 attr: *const igraph_attribute_record_list_t,
615) -> igraph_error_t {
616 if unsafe { (*graph).attr.is_null() } {
617 return igraph_error_type_t_IGRAPH_SUCCESS;
618 }
619 match c_handler!(add_edges) {
620 Some(f) => unsafe { f(graph, edges, attr) },
621 None => missing_callback(),
622 }
623}
624
625unsafe fn both_have_storage(graph: *const igraph_t, newgraph: *const igraph_t) -> bool {
626 unsafe { !(*graph).attr.is_null() && !(*newgraph).attr.is_null() }
627}
628
629unsafe extern "C" fn g_permute_vertices(
630 graph: *const igraph_t,
631 newgraph: *mut igraph_t,
632 idx: *const igraph_vector_int_t,
633) -> igraph_error_t {
634 if unsafe { !both_have_storage(graph, newgraph) } {
635 return igraph_error_type_t_IGRAPH_SUCCESS;
636 }
637 match c_handler!(permute_vertices) {
638 Some(f) => unsafe { f(graph, newgraph, idx) },
639 None => missing_callback(),
640 }
641}
642
643unsafe extern "C" fn g_permute_edges(
644 graph: *const igraph_t,
645 newgraph: *mut igraph_t,
646 idx: *const igraph_vector_int_t,
647) -> igraph_error_t {
648 if unsafe { !both_have_storage(graph, newgraph) } {
649 return igraph_error_type_t_IGRAPH_SUCCESS;
650 }
651 match c_handler!(permute_edges) {
652 Some(f) => unsafe { f(graph, newgraph, idx) },
653 None => missing_callback(),
654 }
655}
656
657unsafe extern "C" fn g_combine_vertices(
658 graph: *const igraph_t,
659 newgraph: *mut igraph_t,
660 merges: *const igraph_vector_int_list_t,
661 comb: *const igraph_attribute_combination_t,
662) -> igraph_error_t {
663 if unsafe { !both_have_storage(graph, newgraph) } {
664 return igraph_error_type_t_IGRAPH_SUCCESS;
665 }
666 match c_handler!(combine_vertices) {
667 Some(f) => unsafe { f(graph, newgraph, merges, comb) },
668 None => missing_callback(),
669 }
670}
671
672unsafe extern "C" fn g_combine_edges(
673 graph: *const igraph_t,
674 newgraph: *mut igraph_t,
675 merges: *const igraph_vector_int_list_t,
676 comb: *const igraph_attribute_combination_t,
677) -> igraph_error_t {
678 if unsafe { !both_have_storage(graph, newgraph) } {
679 return igraph_error_type_t_IGRAPH_SUCCESS;
680 }
681 match c_handler!(combine_edges) {
682 Some(f) => unsafe { f(graph, newgraph, merges, comb) },
683 None => missing_callback(),
684 }
685}
686
687unsafe extern "C" fn g_get_info(
688 graph: *const igraph_t,
689 gnames: *mut igraph_strvector_t,
690 gtypes: *mut igraph_vector_int_t,
691 vnames: *mut igraph_strvector_t,
692 vtypes: *mut igraph_vector_int_t,
693 enames: *mut igraph_strvector_t,
694 etypes: *mut igraph_vector_int_t,
695) -> igraph_error_t {
696 if unsafe { (*graph).attr.is_null() } {
697 for names in [gnames, vnames, enames] {
698 if !names.is_null() {
699 unsafe { igraph_strvector_clear(names) };
700 }
701 }
702 for types in [gtypes, vtypes, etypes] {
703 if !types.is_null() {
704 unsafe { igraph_vector_int_clear(types) };
705 }
706 }
707 return igraph_error_type_t_IGRAPH_SUCCESS;
708 }
709 match c_handler!(get_info) {
710 Some(f) => unsafe { f(graph, gnames, gtypes, vnames, vtypes, enames, etypes) },
711 None => missing_callback(),
712 }
713}
714
715unsafe extern "C" fn g_has_attr(
716 graph: *const igraph_t,
717 kind: igraph_attribute_elemtype_t,
718 name: *const c_char,
719) -> igraph_bool_t {
720 if unsafe { (*graph).attr.is_null() } {
721 return false;
722 }
723 match c_handler!(has_attr) {
724 Some(f) => unsafe { f(graph, kind, name) },
725 None => false,
726 }
727}
728
729unsafe extern "C" fn g_get_type(
730 graph: *const igraph_t,
731 type_: *mut igraph_attribute_type_t,
732 elemtype: igraph_attribute_elemtype_t,
733 name: *const c_char,
734) -> igraph_error_t {
735 if unsafe { (*graph).attr.is_null() } {
736 return no_such_attribute();
737 }
738 match c_handler!(get_type) {
739 Some(f) => unsafe { f(graph, type_, elemtype, name) },
740 None => missing_callback(),
741 }
742}
743
744macro_rules! guarded_graph_getter {
745 ($name:ident, $field:ident, $out:ty) => {
746 unsafe extern "C" fn $name(
747 graph: *const igraph_t,
748 name: *const c_char,
749 value: *mut $out,
750 ) -> igraph_error_t {
751 if unsafe { (*graph).attr.is_null() } {
752 return no_such_attribute();
753 }
754 match c_handler!($field) {
755 Some(f) => unsafe { f(graph, name, value) },
756 None => missing_callback(),
757 }
758 }
759 };
760}
761
762macro_rules! guarded_elem_getter {
763 ($name:ident, $field:ident, $sel:ty, $out:ty) => {
764 unsafe extern "C" fn $name(
765 graph: *const igraph_t,
766 name: *const c_char,
767 sel: $sel,
768 value: *mut $out,
769 ) -> igraph_error_t {
770 if unsafe { (*graph).attr.is_null() } {
771 return no_such_attribute();
772 }
773 match c_handler!($field) {
774 Some(f) => unsafe { f(graph, name, sel, value) },
775 None => missing_callback(),
776 }
777 }
778 };
779}
780
781guarded_graph_getter!(g_get_num_g, get_numeric_graph_attr, igraph_vector_t);
782guarded_graph_getter!(g_get_str_g, get_string_graph_attr, igraph_strvector_t);
783guarded_graph_getter!(g_get_bool_g, get_bool_graph_attr, igraph_vector_bool_t);
784guarded_elem_getter!(
785 g_get_num_v,
786 get_numeric_vertex_attr,
787 igraph_vs_t,
788 igraph_vector_t
789);
790guarded_elem_getter!(
791 g_get_str_v,
792 get_string_vertex_attr,
793 igraph_vs_t,
794 igraph_strvector_t
795);
796guarded_elem_getter!(
797 g_get_bool_v,
798 get_bool_vertex_attr,
799 igraph_vs_t,
800 igraph_vector_bool_t
801);
802guarded_elem_getter!(
803 g_get_num_e,
804 get_numeric_edge_attr,
805 igraph_es_t,
806 igraph_vector_t
807);
808guarded_elem_getter!(
809 g_get_str_e,
810 get_string_edge_attr,
811 igraph_es_t,
812 igraph_strvector_t
813);
814guarded_elem_getter!(
815 g_get_bool_e,
816 get_bool_edge_attr,
817 igraph_es_t,
818 igraph_vector_bool_t
819);
820
821/// igraph's C attribute handler, guarded against attribute-less graphs.
822static GUARDED_TABLE: igraph_attribute_table_t = igraph_attribute_table_t {
823 init: Some(g_init),
824 destroy: Some(g_destroy),
825 copy: Some(g_copy),
826 add_vertices: Some(g_add_vertices),
827 permute_vertices: Some(g_permute_vertices),
828 combine_vertices: Some(g_combine_vertices),
829 add_edges: Some(g_add_edges),
830 permute_edges: Some(g_permute_edges),
831 combine_edges: Some(g_combine_edges),
832 get_info: Some(g_get_info),
833 has_attr: Some(g_has_attr),
834 get_type: Some(g_get_type),
835 get_numeric_graph_attr: Some(g_get_num_g),
836 get_string_graph_attr: Some(g_get_str_g),
837 get_bool_graph_attr: Some(g_get_bool_g),
838 get_numeric_vertex_attr: Some(g_get_num_v),
839 get_string_vertex_attr: Some(g_get_str_v),
840 get_bool_vertex_attr: Some(g_get_bool_v),
841 get_numeric_edge_attr: Some(g_get_num_e),
842 get_string_edge_attr: Some(g_get_str_e),
843 get_bool_edge_attr: Some(g_get_bool_e),
844};
845
846static ENABLE: Once = Once::new();
847static ENABLED: AtomicBool = AtomicBool::new(false);
848
849/// Turns on igraph's C attribute handler for the whole process.
850///
851/// This attaches igraph's C attribute handler (`igraph_cattribute_table`,
852/// in a variant guarded against graphs without attribute storage) with
853/// [`igraph_set_attribute_table`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_set_attribute_table).
854/// From then on every newly created graph carries attribute storage, and
855/// igraph keeps graph, vertex and edge attributes in sync with every
856/// structural change.
857///
858/// - It is idempotent (cheap after the first call) and **irreversible**.
859/// - The setting is process-wide, not per thread: call it early, ideally at
860/// the start of `main`, before other threads use igraph.
861/// - Attribute setters call it implicitly; call it explicitly before reading
862/// graphs from files whose attributes (vertex names, edge weights, ...) you
863/// want to keep.
864/// - Graphs created before enabling keep working; they simply have no
865/// attributes until the first setter creates their storage.
866///
867/// # Errors
868///
869/// [`ErrorKind::Exists`] if a different attribute handler was attached
870/// beforehand through the raw FFI; that handler is left in place.
871///
872/// # Examples
873///
874/// ```
875/// use igraph::attributes;
876/// attributes::enable().unwrap();
877/// attributes::enable().unwrap(); // no-op
878/// assert!(attributes::is_enabled());
879/// assert!(attributes::has_attribute_table());
880/// ```
881pub fn enable() -> Result<()> {
882 ensure_init();
883 ENABLE.call_once(|| {
884 let ours: *const igraph_attribute_table_t = &GUARDED_TABLE;
885 let old = unsafe { igraph_set_attribute_table(ours) };
886 if old.is_null() || ptr::eq(old, ours) {
887 ENABLED.store(true, Ordering::Release);
888 } else {
889 // Someone attached another handler: put it back.
890 unsafe { igraph_set_attribute_table(old) };
891 }
892 });
893 if is_enabled() {
894 Ok(())
895 } else {
896 Err(Error::new(
897 ErrorKind::Exists,
898 "another attribute handler was attached through the raw FFI",
899 ))
900 }
901}
902
903/// Whether this crate's attribute handler is attached (see [`enable`]).
904pub fn is_enabled() -> bool {
905 ENABLED.load(Ordering::Acquire)
906}
907
908/// Whether *some* attribute handler is attached to igraph
909/// ([`igraph_has_attribute_table`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_has_attribute_table)).
910///
911/// This is `true` after [`enable`], but also when a handler was attached
912/// through the raw FFI.
913pub fn has_attribute_table() -> bool {
914 unsafe { igraph_has_attribute_table() }
915}
916
917// ---------------------------------------------------------------------------
918// Helpers
919// ---------------------------------------------------------------------------
920
921fn c_string(what: &str, s: &str) -> Result<CString> {
922 CString::new(s).map_err(|_| Error::invalid(format!("the {what} {s:?} contains a NUL byte")))
923}
924
925fn missing(kind: AttributeKind, name: &str) -> Error {
926 Error::invalid(format!("{kind} attribute '{name}' does not exist"))
927}
928
929fn lossy(ptr: *const c_char) -> String {
930 if ptr.is_null() {
931 String::new()
932 } else {
933 unsafe { CStr::from_ptr(ptr) }
934 .to_string_lossy()
935 .into_owned()
936 }
937}
938
939fn to_strvector<S: AsRef<str>>(values: &[S]) -> Result<StrVector> {
940 let mut sv = StrVector::new();
941 for s in values {
942 let s = s.as_ref();
943 if s.contains('\0') {
944 return Err(Error::invalid(format!(
945 "the string value {s:?} contains a NUL byte"
946 )));
947 }
948 sv.push(s);
949 }
950 Ok(sv)
951}
952
953fn raw_type(raw: igraph_int_t) -> AttributeType {
954 AttributeType::try_from(raw as igraph_attribute_type_t).unwrap_or(AttributeType::Object)
955}
956
957/// An owned `igraph_attribute_record_list_t`, used to hand records to igraph.
958struct RecordList(igraph_attribute_record_list_t);
959
960impl RecordList {
961 fn from_records(records: &[AttributeRecord]) -> Result<Self> {
962 let mut raw = MaybeUninit::<igraph_attribute_record_list_t>::uninit();
963 igraph_call!(igraph_attribute_record_list_init(raw.as_mut_ptr(), 0))?;
964 let mut list = RecordList(unsafe { raw.assume_init() });
965 for rec in records {
966 if rec.name().is_none() {
967 return Err(Error::invalid("attribute records must have a name"));
968 }
969 // igraph's C handler aborts (fatal error) on untyped records.
970 if !matches!(
971 rec.attribute_type(),
972 AttributeType::Numeric | AttributeType::Boolean | AttributeType::String
973 ) {
974 return Err(Error::invalid(format!(
975 "attribute record '{}' has no supported type",
976 rec.name().unwrap_or_default()
977 )));
978 }
979 igraph_call!(igraph_attribute_record_list_push_back_copy(
980 &mut list.0,
981 rec
982 ))?;
983 }
984 Ok(list)
985 }
986}
987
988impl Drop for RecordList {
989 fn drop(&mut self) {
990 unsafe { igraph_attribute_record_list_destroy(&mut self.0) };
991 }
992}
993
994// ---------------------------------------------------------------------------
995// Graph methods
996// ---------------------------------------------------------------------------
997
998impl igraph_t {
999 /// Whether this graph can be read through the C attribute handler.
1000 fn has_attr_storage(&self) -> bool {
1001 is_enabled() && !self.attr.is_null()
1002 }
1003
1004 /// Enables attributes and makes sure this graph has attribute storage.
1005 fn ensure_attr_storage(&mut self) -> Result<()> {
1006 enable()?;
1007 if self.attr.is_null() {
1008 igraph_call!(g_init(self, ptr::null()))?;
1009 }
1010 Ok(())
1011 }
1012
1013 fn check_vid(&self, vid: VertexId) -> Result<()> {
1014 if vid < 0 || vid as usize >= self.vcount() {
1015 return Err(Error::new(
1016 ErrorKind::InvalidVertexId,
1017 format!("vertex id {vid} is out of range 0..{}", self.vcount()),
1018 ));
1019 }
1020 Ok(())
1021 }
1022
1023 fn check_eid(&self, eid: EdgeId) -> Result<()> {
1024 if eid < 0 || eid as usize >= self.ecount() {
1025 return Err(Error::new(
1026 ErrorKind::InvalidEdgeId,
1027 format!("edge id {eid} is out of range 0..{}", self.ecount()),
1028 ));
1029 }
1030 Ok(())
1031 }
1032
1033 /// Checks that attribute `name` of the given kind exists with type `expected`.
1034 fn expect_type(&self, kind: AttributeKind, name: &str, expected: AttributeType) -> Result<()> {
1035 match self.attribute_type(kind, name)? {
1036 None => Err(missing(kind, name)),
1037 Some(t) if t == expected => Ok(()),
1038 Some(t) => Err(Error::invalid(format!(
1039 "{kind} attribute '{name}' is {t}, not {expected}"
1040 ))),
1041 }
1042 }
1043
1044 // --- listing --------------------------------------------------------------
1045
1046 /// Names and types of all the graph, vertex and edge attributes
1047 /// ([`igraph_cattribute_list`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_list)).
1048 ///
1049 /// Attributes are listed in creation order. A graph without attribute
1050 /// storage (created before [`enable`]) has an empty list.
1051 ///
1052 /// Time complexity: O(Ag+Av+Ae), the total number of attributes.
1053 ///
1054 /// # Examples
1055 ///
1056 /// ```
1057 /// use igraph::prelude::*;
1058 /// use igraph::attributes::AttributeType;
1059 ///
1060 /// let mut g = Graph::new(2, false);
1061 /// g.set_graph_attr_numeric("year", 2024.0).unwrap();
1062 /// g.set_vertex_attr_str("label", 0, "a").unwrap();
1063 /// let list = g.attribute_list().unwrap();
1064 /// assert_eq!(list.graph, vec![("year".to_string(), AttributeType::Numeric)]);
1065 /// assert_eq!(list.vertex, vec![("label".to_string(), AttributeType::String)]);
1066 /// assert!(list.edge.is_empty());
1067 /// ```
1068 pub fn attribute_list(&self) -> Result<AttributeList> {
1069 if !self.has_attr_storage() {
1070 return Ok(AttributeList::default());
1071 }
1072 let (mut gn, mut vn, mut en) = (StrVector::new(), StrVector::new(), StrVector::new());
1073 let (mut gt, mut vt, mut et) = (VectorInt::new(), VectorInt::new(), VectorInt::new());
1074 igraph_call!(igraph_cattribute_list(
1075 self, &mut gn, &mut gt, &mut vn, &mut vt, &mut en, &mut et
1076 ))?;
1077 let zip = |n: &StrVector, t: &VectorInt| -> Vec<(String, AttributeType)> {
1078 n.iter()
1079 .zip(t.iter())
1080 .map(|(n, &t)| (n, raw_type(t)))
1081 .collect()
1082 };
1083 Ok(AttributeList {
1084 graph: zip(&gn, >),
1085 vertex: zip(&vn, &vt),
1086 edge: zip(&en, &et),
1087 })
1088 }
1089
1090 /// Names of the attributes of one kind, in creation order (from
1091 /// [`igraph_cattribute_list`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_list)).
1092 pub fn attribute_names(&self, kind: AttributeKind) -> Result<Vec<String>> {
1093 Ok(self
1094 .attribute_list()?
1095 .of(kind)
1096 .iter()
1097 .map(|(n, _)| n.clone())
1098 .collect())
1099 }
1100
1101 /// The type of an attribute, or `None` if there is no such attribute
1102 /// (from [`igraph_cattribute_list`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_list)).
1103 ///
1104 /// ```
1105 /// use igraph::prelude::*;
1106 /// use igraph::attributes::{AttributeKind, AttributeType};
1107 ///
1108 /// let mut g = Graph::from_edges(&[(0, 1)], 2, false).unwrap();
1109 /// g.set_edge_attr_bool("bridge", 0, true).unwrap();
1110 /// assert_eq!(g.attribute_type(AttributeKind::Edge, "bridge").unwrap(), Some(AttributeType::Boolean));
1111 /// assert_eq!(g.attribute_type(AttributeKind::Vertex, "bridge").unwrap(), None);
1112 /// ```
1113 pub fn attribute_type(&self, kind: AttributeKind, name: &str) -> Result<Option<AttributeType>> {
1114 if !self.has_attr_storage() {
1115 return Ok(None);
1116 }
1117 let mut names = StrVector::new();
1118 let mut types = VectorInt::new();
1119 let (n, t) = (&mut names as *mut StrVector, &mut types as *mut VectorInt);
1120 let null_s = ptr::null_mut::<igraph_strvector_t>();
1121 let null_t = ptr::null_mut::<igraph_vector_int_t>();
1122 let args = match kind {
1123 AttributeKind::Graph => (n, t, null_s, null_t, null_s, null_t),
1124 AttributeKind::Vertex => (null_s, null_t, n, t, null_s, null_t),
1125 AttributeKind::Edge => (null_s, null_t, null_s, null_t, n, t),
1126 };
1127 igraph_call!(igraph_cattribute_list(
1128 self, args.0, args.1, args.2, args.3, args.4, args.5
1129 ))?;
1130 Ok((0..names.len())
1131 .find(|&i| {
1132 names
1133 .get_cstr(i)
1134 .is_some_and(|c| c.to_bytes() == name.as_bytes())
1135 })
1136 .map(|i| raw_type(types[i])))
1137 }
1138
1139 /// Whether the graph has a graph, vertex or edge attribute called `name`
1140 /// ([`igraph_cattribute_has_attr`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_has_attr)).
1141 ///
1142 /// Always `false` for graphs without attribute storage (and for names
1143 /// containing a NUL byte).
1144 ///
1145 /// Time complexity: O(A), the number of attributes of that kind.
1146 ///
1147 /// ```
1148 /// use igraph::prelude::*;
1149 /// use igraph::attributes::AttributeKind;
1150 /// let mut g = Graph::new(1, false);
1151 /// g.set_vertex_attr_str("name", 0, "solo").unwrap();
1152 /// assert!(g.has_attribute(AttributeKind::Vertex, "name"));
1153 /// assert!(!g.has_attribute(AttributeKind::Graph, "name"));
1154 /// ```
1155 pub fn has_attribute(&self, kind: AttributeKind, name: &str) -> bool {
1156 if !self.has_attr_storage() {
1157 return false;
1158 }
1159 match CString::new(name) {
1160 Ok(c) => unsafe { igraph_cattribute_has_attr(self, kind.into(), c.as_ptr()) },
1161 Err(_) => false,
1162 }
1163 }
1164
1165 // --- graph attributes -----------------------------------------------------
1166
1167 /// The value of a numeric graph attribute
1168 /// ([`igraph_cattribute_GAN`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_GAN)).
1169 ///
1170 /// # Errors
1171 ///
1172 /// [`ErrorKind::InvalidValue`] if the attribute does not exist or is not
1173 /// numeric (the C function would only emit a warning and return `NaN`).
1174 ///
1175 /// ```
1176 /// use igraph::prelude::*;
1177 /// let mut g = Graph::new(0, false);
1178 /// g.set_graph_attr_numeric("density", 0.25).unwrap();
1179 /// assert_eq!(g.graph_attr_numeric("density").unwrap(), 0.25);
1180 /// assert_eq!(g.graph_attr_numeric("nope").unwrap_err().kind(), ErrorKind::InvalidValue);
1181 /// ```
1182 pub fn graph_attr_numeric(&self, name: &str) -> Result<f64> {
1183 let c = c_string("attribute name", name)?;
1184 self.expect_type(AttributeKind::Graph, name, AttributeType::Numeric)?;
1185 Ok(unsafe { igraph_cattribute_GAN(self, c.as_ptr()) })
1186 }
1187
1188 /// The value of a boolean graph attribute
1189 /// ([`igraph_cattribute_GAB`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_GAB)).
1190 ///
1191 /// # Errors
1192 ///
1193 /// [`ErrorKind::InvalidValue`] if the attribute does not exist or is not boolean.
1194 pub fn graph_attr_bool(&self, name: &str) -> Result<bool> {
1195 let c = c_string("attribute name", name)?;
1196 self.expect_type(AttributeKind::Graph, name, AttributeType::Boolean)?;
1197 Ok(unsafe { igraph_cattribute_GAB(self, c.as_ptr()) })
1198 }
1199
1200 /// The value of a string graph attribute, copied into a [`String`]
1201 /// ([`igraph_cattribute_GAS`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_GAS)).
1202 ///
1203 /// Invalid UTF-8 is replaced lossily.
1204 ///
1205 /// # Errors
1206 ///
1207 /// [`ErrorKind::InvalidValue`] if the attribute does not exist or is not a string.
1208 pub fn graph_attr_str(&self, name: &str) -> Result<String> {
1209 let c = c_string("attribute name", name)?;
1210 self.expect_type(AttributeKind::Graph, name, AttributeType::String)?;
1211 Ok(lossy(unsafe { igraph_cattribute_GAS(self, c.as_ptr()) }))
1212 }
1213
1214 /// The value of a graph attribute, whatever its type (`GAN`/`GAB`/`GAS`).
1215 ///
1216 /// # Errors
1217 ///
1218 /// [`ErrorKind::InvalidValue`] if the attribute does not exist.
1219 pub fn graph_attr(&self, name: &str) -> Result<AttributeValue> {
1220 match self.attribute_type(AttributeKind::Graph, name)? {
1221 Some(AttributeType::Numeric) => {
1222 self.graph_attr_numeric(name).map(AttributeValue::Numeric)
1223 }
1224 Some(AttributeType::Boolean) => self.graph_attr_bool(name).map(AttributeValue::Boolean),
1225 Some(AttributeType::String) => self.graph_attr_str(name).map(AttributeValue::String),
1226 _ => Err(missing(AttributeKind::Graph, name)),
1227 }
1228 }
1229
1230 /// Sets a numeric graph attribute, creating it if needed
1231 /// ([`igraph_cattribute_GAN_set`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_GAN_set)).
1232 ///
1233 /// Enables attributes (see [`enable`]) if they are not yet.
1234 ///
1235 /// # Errors
1236 ///
1237 /// [`ErrorKind::InvalidValue`] if the attribute exists with another type.
1238 pub fn set_graph_attr_numeric(&mut self, name: &str, value: f64) -> Result<()> {
1239 let c = c_string("attribute name", name)?;
1240 self.ensure_attr_storage()?;
1241 igraph_call!(igraph_cattribute_GAN_set(self, c.as_ptr(), value))
1242 }
1243
1244 /// Sets a boolean graph attribute, creating it if needed
1245 /// ([`igraph_cattribute_GAB_set`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_GAB_set)).
1246 ///
1247 /// # Errors
1248 ///
1249 /// [`ErrorKind::InvalidValue`] if the attribute exists with another type.
1250 pub fn set_graph_attr_bool(&mut self, name: &str, value: bool) -> Result<()> {
1251 let c = c_string("attribute name", name)?;
1252 self.ensure_attr_storage()?;
1253 igraph_call!(igraph_cattribute_GAB_set(self, c.as_ptr(), value))
1254 }
1255
1256 /// Sets a string graph attribute (the value is copied), creating it if
1257 /// needed ([`igraph_cattribute_GAS_set`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_GAS_set)).
1258 ///
1259 /// # Errors
1260 ///
1261 /// [`ErrorKind::InvalidValue`] if the attribute exists with another type,
1262 /// or if `name` or `value` contain a NUL byte.
1263 pub fn set_graph_attr_str(&mut self, name: &str, value: &str) -> Result<()> {
1264 let c = c_string("attribute name", name)?;
1265 let v = c_string("string value", value)?;
1266 self.ensure_attr_storage()?;
1267 igraph_call!(igraph_cattribute_GAS_set(self, c.as_ptr(), v.as_ptr()))
1268 }
1269
1270 /// Sets a graph attribute of any type (`GAN_set`/`GAB_set`/`GAS_set`).
1271 ///
1272 /// # Errors
1273 ///
1274 /// As the typed setters, e.g. [`set_graph_attr_numeric`](Self::set_graph_attr_numeric).
1275 ///
1276 /// ```
1277 /// use igraph::prelude::*;
1278 /// use igraph::attributes::AttributeValue;
1279 /// let mut g = Graph::new(0, true);
1280 /// g.set_graph_attr("name", "empty").unwrap();
1281 /// g.set_graph_attr("order", 0).unwrap();
1282 /// assert_eq!(g.graph_attr("name").unwrap(), AttributeValue::String("empty".into()));
1283 /// assert_eq!(g.graph_attr("order").unwrap().as_f64(), Some(0.0));
1284 /// ```
1285 pub fn set_graph_attr(&mut self, name: &str, value: impl Into<AttributeValue>) -> Result<()> {
1286 match value.into() {
1287 AttributeValue::Numeric(x) => self.set_graph_attr_numeric(name, x),
1288 AttributeValue::Boolean(b) => self.set_graph_attr_bool(name, b),
1289 AttributeValue::String(s) => self.set_graph_attr_str(name, &s),
1290 }
1291 }
1292
1293 /// Removes a graph attribute, returning whether it existed
1294 /// ([`igraph_cattribute_remove_g`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_remove_g)).
1295 pub fn remove_graph_attr(&mut self, name: &str) -> bool {
1296 self.remove_attr(AttributeKind::Graph, name)
1297 }
1298
1299 fn remove_attr(&mut self, kind: AttributeKind, name: &str) -> bool {
1300 if !self.has_attribute(kind, name) {
1301 return false;
1302 }
1303 let Ok(c) = CString::new(name) else {
1304 return false;
1305 };
1306 unsafe {
1307 match kind {
1308 AttributeKind::Graph => igraph_cattribute_remove_g(self, c.as_ptr()),
1309 AttributeKind::Vertex => igraph_cattribute_remove_v(self, c.as_ptr()),
1310 AttributeKind::Edge => igraph_cattribute_remove_e(self, c.as_ptr()),
1311 }
1312 }
1313 true
1314 }
1315
1316 /// Removes all the graph, vertex and/or edge attributes
1317 /// ([`igraph_cattribute_remove_all`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_remove_all)).
1318 ///
1319 /// ```
1320 /// use igraph::prelude::*;
1321 /// let mut g = Graph::new(3, false);
1322 /// g.set_graph_attr_str("title", "t").unwrap();
1323 /// g.set_vertex_attr_numeric("x", 0, 1.0).unwrap();
1324 /// g.remove_all_attributes(false, true, true);
1325 /// let list = g.attribute_list().unwrap();
1326 /// assert_eq!(list.graph.len(), 1);
1327 /// assert!(list.vertex.is_empty());
1328 /// ```
1329 pub fn remove_all_attributes(&mut self, graph: bool, vertex: bool, edge: bool) {
1330 if self.has_attr_storage() {
1331 unsafe { igraph_cattribute_remove_all(self, graph, vertex, edge) }
1332 }
1333 }
1334
1335 // --- vertex attributes ----------------------------------------------------
1336
1337 /// The value of a numeric vertex attribute for one vertex
1338 /// ([`igraph_cattribute_VAN`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_VAN)).
1339 ///
1340 /// Vertices that never had the attribute set hold `NaN`.
1341 ///
1342 /// # Errors
1343 ///
1344 /// [`ErrorKind::InvalidVertexId`] for an invalid vertex,
1345 /// [`ErrorKind::InvalidValue`] if the attribute does not exist or is not numeric.
1346 ///
1347 /// ```
1348 /// use igraph::prelude::*;
1349 /// let mut g = Graph::new(3, false);
1350 /// g.set_vertex_attr_numeric("age", 1, 42.0).unwrap();
1351 /// assert_eq!(g.vertex_attr_numeric("age", 1).unwrap(), 42.0);
1352 /// assert!(g.vertex_attr_numeric("age", 0).unwrap().is_nan());
1353 /// assert_eq!(g.vertex_attr_numeric("age", 3).unwrap_err().kind(), ErrorKind::InvalidVertexId);
1354 /// ```
1355 pub fn vertex_attr_numeric(&self, name: &str, vid: VertexId) -> Result<f64> {
1356 let c = c_string("attribute name", name)?;
1357 self.check_vid(vid)?;
1358 self.expect_type(AttributeKind::Vertex, name, AttributeType::Numeric)?;
1359 Ok(unsafe { igraph_cattribute_VAN(self, c.as_ptr(), vid) })
1360 }
1361
1362 /// The value of a boolean vertex attribute for one vertex
1363 /// ([`igraph_cattribute_VAB`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_VAB)).
1364 ///
1365 /// # Errors
1366 ///
1367 /// [`ErrorKind::InvalidVertexId`] for an invalid vertex,
1368 /// [`ErrorKind::InvalidValue`] if the attribute does not exist or is not boolean.
1369 pub fn vertex_attr_bool(&self, name: &str, vid: VertexId) -> Result<bool> {
1370 let c = c_string("attribute name", name)?;
1371 self.check_vid(vid)?;
1372 self.expect_type(AttributeKind::Vertex, name, AttributeType::Boolean)?;
1373 Ok(unsafe { igraph_cattribute_VAB(self, c.as_ptr(), vid) })
1374 }
1375
1376 /// The value of a string vertex attribute for one vertex, copied into a
1377 /// [`String`] ([`igraph_cattribute_VAS`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_VAS)).
1378 ///
1379 /// # Errors
1380 ///
1381 /// [`ErrorKind::InvalidVertexId`] for an invalid vertex,
1382 /// [`ErrorKind::InvalidValue`] if the attribute does not exist or is not a string.
1383 pub fn vertex_attr_str(&self, name: &str, vid: VertexId) -> Result<String> {
1384 let c = c_string("attribute name", name)?;
1385 self.check_vid(vid)?;
1386 self.expect_type(AttributeKind::Vertex, name, AttributeType::String)?;
1387 Ok(lossy(unsafe {
1388 igraph_cattribute_VAS(self, c.as_ptr(), vid)
1389 }))
1390 }
1391
1392 /// The value of a vertex attribute of any type for one vertex (`VAN`/`VAB`/`VAS`).
1393 ///
1394 /// # Errors
1395 ///
1396 /// [`ErrorKind::InvalidVertexId`] for an invalid vertex,
1397 /// [`ErrorKind::InvalidValue`] if the attribute does not exist.
1398 pub fn vertex_attr(&self, name: &str, vid: VertexId) -> Result<AttributeValue> {
1399 match self.attribute_type(AttributeKind::Vertex, name)? {
1400 Some(AttributeType::Numeric) => self
1401 .vertex_attr_numeric(name, vid)
1402 .map(AttributeValue::Numeric),
1403 Some(AttributeType::Boolean) => self
1404 .vertex_attr_bool(name, vid)
1405 .map(AttributeValue::Boolean),
1406 Some(AttributeType::String) => {
1407 self.vertex_attr_str(name, vid).map(AttributeValue::String)
1408 }
1409 _ => Err(missing(AttributeKind::Vertex, name)),
1410 }
1411 }
1412
1413 /// The values of a numeric vertex attribute for the selected vertices, in
1414 /// selector order ([`igraph_cattribute_VANV`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_VANV)).
1415 ///
1416 /// Use `..` to select all the vertices.
1417 ///
1418 /// Time complexity: O(v), the number of selected vertices.
1419 ///
1420 /// # Errors
1421 ///
1422 /// [`ErrorKind::InvalidVertexId`] for an invalid vertex,
1423 /// [`ErrorKind::InvalidValue`] if the attribute does not exist or is not numeric.
1424 ///
1425 /// ```
1426 /// use igraph::prelude::*;
1427 /// let mut g = Graph::new(4, false);
1428 /// g.set_vertex_attr_numeric_values("x", &[0.0, 0.5, 1.0, 1.5]).unwrap();
1429 /// assert_eq!(g.vertex_attr_numeric_values("x", vec![3, 0]).unwrap(), vec![1.5, 0.0]);
1430 /// assert_eq!(g.vertex_attr_numeric_values("x", 1..3).unwrap(), vec![0.5, 1.0]);
1431 /// ```
1432 pub fn vertex_attr_numeric_values<'a>(
1433 &self,
1434 name: &str,
1435 vids: impl Into<VertexSelector<'a>>,
1436 ) -> Result<Vec<f64>> {
1437 let c = c_string("attribute name", name)?;
1438 let vs = vids.into().to_raw()?;
1439 if !self.has_attr_storage() {
1440 return Err(missing(AttributeKind::Vertex, name));
1441 }
1442 let mut res = Vector::new();
1443 igraph_call!(igraph_cattribute_VANV(self, c.as_ptr(), vs.get(), &mut res))?;
1444 Ok(res.into())
1445 }
1446
1447 /// The values of a boolean vertex attribute for the selected vertices
1448 /// ([`igraph_cattribute_VABV`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_VABV)).
1449 ///
1450 /// # Errors
1451 ///
1452 /// [`ErrorKind::InvalidVertexId`] for an invalid vertex,
1453 /// [`ErrorKind::InvalidValue`] if the attribute does not exist or is not boolean.
1454 pub fn vertex_attr_bool_values<'a>(
1455 &self,
1456 name: &str,
1457 vids: impl Into<VertexSelector<'a>>,
1458 ) -> Result<Vec<bool>> {
1459 let c = c_string("attribute name", name)?;
1460 let vs = vids.into().to_raw()?;
1461 if !self.has_attr_storage() {
1462 return Err(missing(AttributeKind::Vertex, name));
1463 }
1464 let mut res = VectorBool::new();
1465 igraph_call!(igraph_cattribute_VABV(self, c.as_ptr(), vs.get(), &mut res))?;
1466 Ok(res.into())
1467 }
1468
1469 /// The values of a string vertex attribute for the selected vertices
1470 /// ([`igraph_cattribute_VASV`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_VASV)).
1471 ///
1472 /// # Errors
1473 ///
1474 /// [`ErrorKind::InvalidVertexId`] for an invalid vertex,
1475 /// [`ErrorKind::InvalidValue`] if the attribute does not exist or is not a string.
1476 ///
1477 /// ```
1478 /// use igraph::prelude::*;
1479 /// let mut g = Graph::famous("Krackhardt_Kite").unwrap();
1480 /// let names = ["Andre", "Beverly", "Carol", "Diane", "Ed", "Fernando", "Garth", "Heather", "Ike", "Jane"];
1481 /// g.set_vertex_attr_str_values("name", &names).unwrap();
1482 /// // The names of Jane's neighbors (Jane is the tail of the kite).
1483 /// let jane = VertexSelector::Adjacent { vertex: 9, mode: NeighborMode::All };
1484 /// assert_eq!(g.vertex_attr_str_values("name", jane).unwrap(), vec!["Ike"]);
1485 /// ```
1486 pub fn vertex_attr_str_values<'a>(
1487 &self,
1488 name: &str,
1489 vids: impl Into<VertexSelector<'a>>,
1490 ) -> Result<Vec<String>> {
1491 let c = c_string("attribute name", name)?;
1492 let vs = vids.into().to_raw()?;
1493 if !self.has_attr_storage() {
1494 return Err(missing(AttributeKind::Vertex, name));
1495 }
1496 let mut res = StrVector::new();
1497 igraph_call!(igraph_cattribute_VASV(self, c.as_ptr(), vs.get(), &mut res))?;
1498 Ok(res.to_vec())
1499 }
1500
1501 /// The values of a vertex attribute of any type for the selected vertices
1502 /// (`VANV`/`VABV`/`VASV`).
1503 ///
1504 /// # Errors
1505 ///
1506 /// [`ErrorKind::InvalidVertexId`] for an invalid vertex,
1507 /// [`ErrorKind::InvalidValue`] if the attribute does not exist.
1508 pub fn vertex_attr_values<'a>(
1509 &self,
1510 name: &str,
1511 vids: impl Into<VertexSelector<'a>>,
1512 ) -> Result<AttributeValues> {
1513 match self.attribute_type(AttributeKind::Vertex, name)? {
1514 Some(AttributeType::Numeric) => self
1515 .vertex_attr_numeric_values(name, vids)
1516 .map(AttributeValues::Numeric),
1517 Some(AttributeType::Boolean) => self
1518 .vertex_attr_bool_values(name, vids)
1519 .map(AttributeValues::Boolean),
1520 Some(AttributeType::String) => self
1521 .vertex_attr_str_values(name, vids)
1522 .map(AttributeValues::String),
1523 _ => Err(missing(AttributeKind::Vertex, name)),
1524 }
1525 }
1526
1527 /// Sets a numeric vertex attribute for one vertex
1528 /// ([`igraph_cattribute_VAN_set`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_VAN_set)).
1529 ///
1530 /// A new attribute is created for all the vertices, with `NaN` for the
1531 /// others. Enables attributes (see [`enable`]) if they are not yet.
1532 ///
1533 /// Time complexity: O(n) if the attribute is new, O(1) otherwise.
1534 ///
1535 /// # Errors
1536 ///
1537 /// [`ErrorKind::InvalidVertexId`] for an invalid vertex,
1538 /// [`ErrorKind::InvalidValue`] if the attribute exists with another type.
1539 pub fn set_vertex_attr_numeric(&mut self, name: &str, vid: VertexId, value: f64) -> Result<()> {
1540 let c = c_string("attribute name", name)?;
1541 self.check_vid(vid)?;
1542 self.ensure_attr_storage()?;
1543 igraph_call!(igraph_cattribute_VAN_set(self, c.as_ptr(), vid, value))
1544 }
1545
1546 /// Sets a boolean vertex attribute for one vertex (`false` for the others
1547 /// if new) ([`igraph_cattribute_VAB_set`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_VAB_set)).
1548 ///
1549 /// # Errors
1550 ///
1551 /// [`ErrorKind::InvalidVertexId`] for an invalid vertex,
1552 /// [`ErrorKind::InvalidValue`] if the attribute exists with another type.
1553 pub fn set_vertex_attr_bool(&mut self, name: &str, vid: VertexId, value: bool) -> Result<()> {
1554 let c = c_string("attribute name", name)?;
1555 self.check_vid(vid)?;
1556 self.ensure_attr_storage()?;
1557 igraph_call!(igraph_cattribute_VAB_set(self, c.as_ptr(), vid, value))
1558 }
1559
1560 /// Sets a string vertex attribute for one vertex (`""` for the others if
1561 /// new) ([`igraph_cattribute_VAS_set`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_VAS_set)).
1562 ///
1563 /// # Errors
1564 ///
1565 /// [`ErrorKind::InvalidVertexId`] for an invalid vertex,
1566 /// [`ErrorKind::InvalidValue`] if the attribute exists with another type
1567 /// or a string contains a NUL byte.
1568 pub fn set_vertex_attr_str(&mut self, name: &str, vid: VertexId, value: &str) -> Result<()> {
1569 let c = c_string("attribute name", name)?;
1570 let v = c_string("string value", value)?;
1571 self.check_vid(vid)?;
1572 self.ensure_attr_storage()?;
1573 igraph_call!(igraph_cattribute_VAS_set(self, c.as_ptr(), vid, v.as_ptr()))
1574 }
1575
1576 /// Sets a vertex attribute of any type for one vertex (`VAN_set`/`VAB_set`/`VAS_set`).
1577 ///
1578 /// # Errors
1579 ///
1580 /// As the typed setters, e.g. [`set_vertex_attr_numeric`](Self::set_vertex_attr_numeric).
1581 ///
1582 /// ```
1583 /// use igraph::prelude::*;
1584 /// let mut g = Graph::new(2, false);
1585 /// g.set_vertex_attr("color", 0, "red").unwrap();
1586 /// g.set_vertex_attr("size", 1, 3.5).unwrap();
1587 /// assert_eq!(g.vertex_attr_str_values("color", ..).unwrap(), vec!["red", ""]);
1588 /// ```
1589 pub fn set_vertex_attr(
1590 &mut self,
1591 name: &str,
1592 vid: VertexId,
1593 value: impl Into<AttributeValue>,
1594 ) -> Result<()> {
1595 match value.into() {
1596 AttributeValue::Numeric(x) => self.set_vertex_attr_numeric(name, vid, x),
1597 AttributeValue::Boolean(b) => self.set_vertex_attr_bool(name, vid, b),
1598 AttributeValue::String(s) => self.set_vertex_attr_str(name, vid, &s),
1599 }
1600 }
1601
1602 /// Sets a numeric vertex attribute for all the vertices at once
1603 /// ([`igraph_cattribute_VAN_setv`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_VAN_setv)).
1604 ///
1605 /// `values[i]` is the value of vertex `i`.
1606 ///
1607 /// # Errors
1608 ///
1609 /// [`ErrorKind::InvalidValue`] if `values.len()` is not the number of
1610 /// vertices or the attribute exists with another type.
1611 pub fn set_vertex_attr_numeric_values(&mut self, name: &str, values: &[f64]) -> Result<()> {
1612 let c = c_string("attribute name", name)?;
1613 self.ensure_attr_storage()?;
1614 let v = Vector::view(values);
1615 igraph_call!(igraph_cattribute_VAN_setv(self, c.as_ptr(), v.as_ptr()))
1616 }
1617
1618 /// Sets a boolean vertex attribute for all the vertices at once
1619 /// ([`igraph_cattribute_VAB_setv`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_VAB_setv)).
1620 ///
1621 /// # Errors
1622 ///
1623 /// [`ErrorKind::InvalidValue`] if `values.len()` is not the number of
1624 /// vertices or the attribute exists with another type.
1625 pub fn set_vertex_attr_bool_values(&mut self, name: &str, values: &[bool]) -> Result<()> {
1626 let c = c_string("attribute name", name)?;
1627 self.ensure_attr_storage()?;
1628 let v = VectorBool::view(values);
1629 igraph_call!(igraph_cattribute_VAB_setv(self, c.as_ptr(), v.as_ptr()))
1630 }
1631
1632 /// Sets a string vertex attribute for all the vertices at once
1633 /// ([`igraph_cattribute_VAS_setv`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_VAS_setv)).
1634 ///
1635 /// # Errors
1636 ///
1637 /// [`ErrorKind::InvalidValue`] if `values.len()` is not the number of
1638 /// vertices, the attribute exists with another type or a string contains
1639 /// a NUL byte.
1640 pub fn set_vertex_attr_str_values<S: AsRef<str>>(
1641 &mut self,
1642 name: &str,
1643 values: &[S],
1644 ) -> Result<()> {
1645 let c = c_string("attribute name", name)?;
1646 let sv = to_strvector(values)?;
1647 self.ensure_attr_storage()?;
1648 igraph_call!(igraph_cattribute_VAS_setv(self, c.as_ptr(), &sv))
1649 }
1650
1651 /// Sets a vertex attribute of any type for all the vertices at once
1652 /// (`VAN_setv`/`VAB_setv`/`VAS_setv`).
1653 ///
1654 /// # Errors
1655 ///
1656 /// As the typed setters, e.g. [`set_vertex_attr_numeric_values`](Self::set_vertex_attr_numeric_values).
1657 ///
1658 /// ```
1659 /// use igraph::prelude::*;
1660 /// let mut g = Graph::new(3, false);
1661 /// g.set_vertex_attr_values("seen", vec![true, false, true]).unwrap();
1662 /// assert_eq!(g.vertex_attr_bool_values("seen", ..).unwrap(), vec![true, false, true]);
1663 /// assert!(g.set_vertex_attr_values("seen", vec![true]).is_err()); // wrong length
1664 /// ```
1665 pub fn set_vertex_attr_values(
1666 &mut self,
1667 name: &str,
1668 values: impl Into<AttributeValues>,
1669 ) -> Result<()> {
1670 match values.into() {
1671 AttributeValues::Numeric(v) => self.set_vertex_attr_numeric_values(name, &v),
1672 AttributeValues::Boolean(v) => self.set_vertex_attr_bool_values(name, &v),
1673 AttributeValues::String(v) => self.set_vertex_attr_str_values(name, &v),
1674 }
1675 }
1676
1677 /// Removes a vertex attribute, returning whether it existed
1678 /// ([`igraph_cattribute_remove_v`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_remove_v)).
1679 ///
1680 /// Once removed, the name can be reused for an attribute of another type.
1681 ///
1682 /// ```
1683 /// use igraph::prelude::*;
1684 /// let mut g = Graph::new(2, false);
1685 /// g.set_vertex_attr_numeric("tmp", 0, 1.0).unwrap();
1686 /// assert!(g.remove_vertex_attr("tmp"));
1687 /// assert!(!g.remove_vertex_attr("tmp"));
1688 /// g.set_vertex_attr_str("tmp", 0, "now a string").unwrap();
1689 /// ```
1690 pub fn remove_vertex_attr(&mut self, name: &str) -> bool {
1691 self.remove_attr(AttributeKind::Vertex, name)
1692 }
1693
1694 // --- edge attributes ------------------------------------------------------
1695
1696 /// The value of a numeric edge attribute for one edge
1697 /// ([`igraph_cattribute_EAN`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_EAN)).
1698 ///
1699 /// # Errors
1700 ///
1701 /// [`ErrorKind::InvalidEdgeId`] for an invalid edge,
1702 /// [`ErrorKind::InvalidValue`] if the attribute does not exist or is not numeric.
1703 pub fn edge_attr_numeric(&self, name: &str, eid: EdgeId) -> Result<f64> {
1704 let c = c_string("attribute name", name)?;
1705 self.check_eid(eid)?;
1706 self.expect_type(AttributeKind::Edge, name, AttributeType::Numeric)?;
1707 Ok(unsafe { igraph_cattribute_EAN(self, c.as_ptr(), eid) })
1708 }
1709
1710 /// The value of a boolean edge attribute for one edge
1711 /// ([`igraph_cattribute_EAB`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_EAB)).
1712 ///
1713 /// # Errors
1714 ///
1715 /// [`ErrorKind::InvalidEdgeId`] for an invalid edge,
1716 /// [`ErrorKind::InvalidValue`] if the attribute does not exist or is not boolean.
1717 pub fn edge_attr_bool(&self, name: &str, eid: EdgeId) -> Result<bool> {
1718 let c = c_string("attribute name", name)?;
1719 self.check_eid(eid)?;
1720 self.expect_type(AttributeKind::Edge, name, AttributeType::Boolean)?;
1721 Ok(unsafe { igraph_cattribute_EAB(self, c.as_ptr(), eid) })
1722 }
1723
1724 /// The value of a string edge attribute for one edge
1725 /// ([`igraph_cattribute_EAS`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_EAS)).
1726 ///
1727 /// # Errors
1728 ///
1729 /// [`ErrorKind::InvalidEdgeId`] for an invalid edge,
1730 /// [`ErrorKind::InvalidValue`] if the attribute does not exist or is not a string.
1731 pub fn edge_attr_str(&self, name: &str, eid: EdgeId) -> Result<String> {
1732 let c = c_string("attribute name", name)?;
1733 self.check_eid(eid)?;
1734 self.expect_type(AttributeKind::Edge, name, AttributeType::String)?;
1735 Ok(lossy(unsafe {
1736 igraph_cattribute_EAS(self, c.as_ptr(), eid)
1737 }))
1738 }
1739
1740 /// The value of an edge attribute of any type for one edge (`EAN`/`EAB`/`EAS`).
1741 ///
1742 /// # Errors
1743 ///
1744 /// [`ErrorKind::InvalidEdgeId`] for an invalid edge,
1745 /// [`ErrorKind::InvalidValue`] if the attribute does not exist.
1746 pub fn edge_attr(&self, name: &str, eid: EdgeId) -> Result<AttributeValue> {
1747 match self.attribute_type(AttributeKind::Edge, name)? {
1748 Some(AttributeType::Numeric) => self
1749 .edge_attr_numeric(name, eid)
1750 .map(AttributeValue::Numeric),
1751 Some(AttributeType::Boolean) => {
1752 self.edge_attr_bool(name, eid).map(AttributeValue::Boolean)
1753 }
1754 Some(AttributeType::String) => {
1755 self.edge_attr_str(name, eid).map(AttributeValue::String)
1756 }
1757 _ => Err(missing(AttributeKind::Edge, name)),
1758 }
1759 }
1760
1761 /// The values of a numeric edge attribute for the selected edges, in
1762 /// selector order ([`igraph_cattribute_EANV`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_EANV)).
1763 ///
1764 /// This is the natural way to obtain a weight vector for the weighted
1765 /// algorithms of the crate: `g.edge_attr_numeric_values("weight", ..)`
1766 /// (see e.g. [`Graph::distances_dijkstra`] and [`Graph::strength`]).
1767 ///
1768 /// Time complexity: O(e), the number of selected edges.
1769 ///
1770 /// # Errors
1771 ///
1772 /// [`ErrorKind::InvalidEdgeId`] for an invalid edge,
1773 /// [`ErrorKind::InvalidValue`] if the attribute does not exist or is not numeric.
1774 ///
1775 /// ```
1776 /// use igraph::prelude::*;
1777 /// let mut g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
1778 /// g.set_edge_attr_numeric_values("weight", &[0.5, 2.0]).unwrap();
1779 /// let weights = g.edge_attr_numeric_values("weight", ..).unwrap();
1780 /// assert_eq!(weights.iter().sum::<f64>(), 2.5);
1781 /// let d = g.distances_dijkstra(0, 2, Some(&weights), NeighborMode::All).unwrap();
1782 /// assert_eq!(d[(0, 0)], 2.5);
1783 /// ```
1784 pub fn edge_attr_numeric_values<'a>(
1785 &self,
1786 name: &str,
1787 eids: impl Into<EdgeSelector<'a>>,
1788 ) -> Result<Vec<f64>> {
1789 let c = c_string("attribute name", name)?;
1790 let es = eids.into().to_raw()?;
1791 if !self.has_attr_storage() {
1792 return Err(missing(AttributeKind::Edge, name));
1793 }
1794 let mut res = Vector::new();
1795 igraph_call!(igraph_cattribute_EANV(self, c.as_ptr(), es.get(), &mut res))?;
1796 Ok(res.into())
1797 }
1798
1799 /// The values of a boolean edge attribute for the selected edges
1800 /// ([`igraph_cattribute_EABV`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_EABV)).
1801 ///
1802 /// # Errors
1803 ///
1804 /// [`ErrorKind::InvalidEdgeId`] for an invalid edge,
1805 /// [`ErrorKind::InvalidValue`] if the attribute does not exist or is not boolean.
1806 pub fn edge_attr_bool_values<'a>(
1807 &self,
1808 name: &str,
1809 eids: impl Into<EdgeSelector<'a>>,
1810 ) -> Result<Vec<bool>> {
1811 let c = c_string("attribute name", name)?;
1812 let es = eids.into().to_raw()?;
1813 if !self.has_attr_storage() {
1814 return Err(missing(AttributeKind::Edge, name));
1815 }
1816 let mut res = VectorBool::new();
1817 igraph_call!(igraph_cattribute_EABV(self, c.as_ptr(), es.get(), &mut res))?;
1818 Ok(res.into())
1819 }
1820
1821 /// The values of a string edge attribute for the selected edges
1822 /// ([`igraph_cattribute_EASV`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_EASV)).
1823 ///
1824 /// # Errors
1825 ///
1826 /// [`ErrorKind::InvalidEdgeId`] for an invalid edge,
1827 /// [`ErrorKind::InvalidValue`] if the attribute does not exist or is not a string.
1828 pub fn edge_attr_str_values<'a>(
1829 &self,
1830 name: &str,
1831 eids: impl Into<EdgeSelector<'a>>,
1832 ) -> Result<Vec<String>> {
1833 let c = c_string("attribute name", name)?;
1834 let es = eids.into().to_raw()?;
1835 if !self.has_attr_storage() {
1836 return Err(missing(AttributeKind::Edge, name));
1837 }
1838 let mut res = StrVector::new();
1839 igraph_call!(igraph_cattribute_EASV(self, c.as_ptr(), es.get(), &mut res))?;
1840 Ok(res.to_vec())
1841 }
1842
1843 /// The values of an edge attribute of any type for the selected edges
1844 /// (`EANV`/`EABV`/`EASV`).
1845 ///
1846 /// # Errors
1847 ///
1848 /// [`ErrorKind::InvalidEdgeId`] for an invalid edge,
1849 /// [`ErrorKind::InvalidValue`] if the attribute does not exist.
1850 pub fn edge_attr_values<'a>(
1851 &self,
1852 name: &str,
1853 eids: impl Into<EdgeSelector<'a>>,
1854 ) -> Result<AttributeValues> {
1855 match self.attribute_type(AttributeKind::Edge, name)? {
1856 Some(AttributeType::Numeric) => self
1857 .edge_attr_numeric_values(name, eids)
1858 .map(AttributeValues::Numeric),
1859 Some(AttributeType::Boolean) => self
1860 .edge_attr_bool_values(name, eids)
1861 .map(AttributeValues::Boolean),
1862 Some(AttributeType::String) => self
1863 .edge_attr_str_values(name, eids)
1864 .map(AttributeValues::String),
1865 _ => Err(missing(AttributeKind::Edge, name)),
1866 }
1867 }
1868
1869 /// Sets a numeric edge attribute for one edge (`NaN` for the others if
1870 /// new) ([`igraph_cattribute_EAN_set`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_EAN_set)).
1871 ///
1872 /// # Errors
1873 ///
1874 /// [`ErrorKind::InvalidEdgeId`] for an invalid edge,
1875 /// [`ErrorKind::InvalidValue`] if the attribute exists with another type.
1876 pub fn set_edge_attr_numeric(&mut self, name: &str, eid: EdgeId, value: f64) -> Result<()> {
1877 let c = c_string("attribute name", name)?;
1878 self.check_eid(eid)?;
1879 self.ensure_attr_storage()?;
1880 igraph_call!(igraph_cattribute_EAN_set(self, c.as_ptr(), eid, value))
1881 }
1882
1883 /// Sets a boolean edge attribute for one edge (`false` for the others if
1884 /// new) ([`igraph_cattribute_EAB_set`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_EAB_set)).
1885 ///
1886 /// # Errors
1887 ///
1888 /// [`ErrorKind::InvalidEdgeId`] for an invalid edge,
1889 /// [`ErrorKind::InvalidValue`] if the attribute exists with another type.
1890 pub fn set_edge_attr_bool(&mut self, name: &str, eid: EdgeId, value: bool) -> Result<()> {
1891 let c = c_string("attribute name", name)?;
1892 self.check_eid(eid)?;
1893 self.ensure_attr_storage()?;
1894 igraph_call!(igraph_cattribute_EAB_set(self, c.as_ptr(), eid, value))
1895 }
1896
1897 /// Sets a string edge attribute for one edge (`""` for the others if new)
1898 /// ([`igraph_cattribute_EAS_set`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_EAS_set)).
1899 ///
1900 /// # Errors
1901 ///
1902 /// [`ErrorKind::InvalidEdgeId`] for an invalid edge,
1903 /// [`ErrorKind::InvalidValue`] if the attribute exists with another type
1904 /// or a string contains a NUL byte.
1905 pub fn set_edge_attr_str(&mut self, name: &str, eid: EdgeId, value: &str) -> Result<()> {
1906 let c = c_string("attribute name", name)?;
1907 let v = c_string("string value", value)?;
1908 self.check_eid(eid)?;
1909 self.ensure_attr_storage()?;
1910 igraph_call!(igraph_cattribute_EAS_set(self, c.as_ptr(), eid, v.as_ptr()))
1911 }
1912
1913 /// Sets an edge attribute of any type for one edge (`EAN_set`/`EAB_set`/`EAS_set`).
1914 ///
1915 /// A new attribute gets the default value (`NaN`, `false` or `""`) on
1916 /// the other edges.
1917 ///
1918 /// # Errors
1919 ///
1920 /// As the typed setters, e.g. [`set_edge_attr_numeric`](Self::set_edge_attr_numeric).
1921 ///
1922 /// ```
1923 /// use igraph::prelude::*;
1924 /// use igraph::attributes::AttributeValue;
1925 /// let mut g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
1926 /// g.set_edge_attr("capacity", 1, 10).unwrap();
1927 /// g.set_edge_attr("road", 0, true).unwrap();
1928 /// assert_eq!(g.edge_attr("capacity", 1).unwrap(), AttributeValue::Numeric(10.0));
1929 /// assert!(g.edge_attr_numeric("capacity", 0).unwrap().is_nan());
1930 /// assert_eq!(g.edge_attr_bool_values("road", ..).unwrap(), vec![true, false]);
1931 /// ```
1932 pub fn set_edge_attr(
1933 &mut self,
1934 name: &str,
1935 eid: EdgeId,
1936 value: impl Into<AttributeValue>,
1937 ) -> Result<()> {
1938 match value.into() {
1939 AttributeValue::Numeric(x) => self.set_edge_attr_numeric(name, eid, x),
1940 AttributeValue::Boolean(b) => self.set_edge_attr_bool(name, eid, b),
1941 AttributeValue::String(s) => self.set_edge_attr_str(name, eid, &s),
1942 }
1943 }
1944
1945 /// Sets a numeric edge attribute for all the edges at once
1946 /// ([`igraph_cattribute_EAN_setv`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_EAN_setv)).
1947 ///
1948 /// `values[i]` is the value of edge `i`.
1949 ///
1950 /// # Errors
1951 ///
1952 /// [`ErrorKind::InvalidValue`] if `values.len()` is not the number of
1953 /// edges or the attribute exists with another type.
1954 pub fn set_edge_attr_numeric_values(&mut self, name: &str, values: &[f64]) -> Result<()> {
1955 let c = c_string("attribute name", name)?;
1956 self.ensure_attr_storage()?;
1957 let v = Vector::view(values);
1958 igraph_call!(igraph_cattribute_EAN_setv(self, c.as_ptr(), v.as_ptr()))
1959 }
1960
1961 /// Sets a boolean edge attribute for all the edges at once
1962 /// ([`igraph_cattribute_EAB_setv`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_EAB_setv)).
1963 ///
1964 /// # Errors
1965 ///
1966 /// [`ErrorKind::InvalidValue`] if `values.len()` is not the number of
1967 /// edges or the attribute exists with another type.
1968 pub fn set_edge_attr_bool_values(&mut self, name: &str, values: &[bool]) -> Result<()> {
1969 let c = c_string("attribute name", name)?;
1970 self.ensure_attr_storage()?;
1971 let v = VectorBool::view(values);
1972 igraph_call!(igraph_cattribute_EAB_setv(self, c.as_ptr(), v.as_ptr()))
1973 }
1974
1975 /// Sets a string edge attribute for all the edges at once
1976 /// ([`igraph_cattribute_EAS_setv`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_EAS_setv)).
1977 ///
1978 /// # Errors
1979 ///
1980 /// [`ErrorKind::InvalidValue`] if `values.len()` is not the number of
1981 /// edges, the attribute exists with another type or a string contains a
1982 /// NUL byte.
1983 pub fn set_edge_attr_str_values<S: AsRef<str>>(
1984 &mut self,
1985 name: &str,
1986 values: &[S],
1987 ) -> Result<()> {
1988 let c = c_string("attribute name", name)?;
1989 let sv = to_strvector(values)?;
1990 self.ensure_attr_storage()?;
1991 igraph_call!(igraph_cattribute_EAS_setv(self, c.as_ptr(), &sv))
1992 }
1993
1994 /// Sets an edge attribute of any type for all the edges at once
1995 /// (`EAN_setv`/`EAB_setv`/`EAS_setv`).
1996 ///
1997 /// # Errors
1998 ///
1999 /// As the typed setters, e.g. [`set_edge_attr_numeric_values`](Self::set_edge_attr_numeric_values).
2000 pub fn set_edge_attr_values(
2001 &mut self,
2002 name: &str,
2003 values: impl Into<AttributeValues>,
2004 ) -> Result<()> {
2005 match values.into() {
2006 AttributeValues::Numeric(v) => self.set_edge_attr_numeric_values(name, &v),
2007 AttributeValues::Boolean(v) => self.set_edge_attr_bool_values(name, &v),
2008 AttributeValues::String(v) => self.set_edge_attr_str_values(name, &v),
2009 }
2010 }
2011
2012 /// Removes an edge attribute, returning whether it existed
2013 /// ([`igraph_cattribute_remove_e`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_cattribute_remove_e)).
2014 pub fn remove_edge_attr(&mut self, name: &str) -> bool {
2015 self.remove_attr(AttributeKind::Edge, name)
2016 }
2017
2018 // --- structure + attributes -----------------------------------------------
2019
2020 /// Adds `n` vertices together with their attribute values
2021 /// ([`igraph_add_vertices`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_add_vertices)
2022 /// with an attribute record list).
2023 ///
2024 /// Each record must be named and hold exactly `n` values. New attributes
2025 /// are created (with default values for the existing vertices);
2026 /// existing attributes not mentioned get their default value for the new
2027 /// vertices. Enables attributes if they are not yet.
2028 ///
2029 /// # Errors
2030 ///
2031 /// [`ErrorKind::InvalidValue`] if a record has the wrong length, no name,
2032 /// or a type different from the existing attribute with the same name.
2033 ///
2034 /// ```
2035 /// use igraph::prelude::*;
2036 /// use igraph::attributes::AttributeRecord;
2037 ///
2038 /// let mut g = Graph::new(1, false);
2039 /// g.set_vertex_attr_str("name", 0, "root").unwrap();
2040 /// let names = AttributeRecord::string("name", &["left", "right"]).unwrap();
2041 /// g.add_vertices_with_attributes(2, &[names]).unwrap();
2042 /// assert_eq!(g.vertex_attr_str_values("name", ..).unwrap(), vec!["root", "left", "right"]);
2043 /// ```
2044 pub fn add_vertices_with_attributes(
2045 &mut self,
2046 n: usize,
2047 records: &[AttributeRecord],
2048 ) -> Result<()> {
2049 let n = igraph_int_t::try_from(n)
2050 .map_err(|_| Error::invalid(format!("cannot add {n} vertices")))?;
2051 let list = RecordList::from_records(records)?;
2052 self.ensure_attr_storage()?;
2053 igraph_call!(igraph_add_vertices(self, n, &list.0))
2054 }
2055
2056 /// Adds edges together with their attribute values
2057 /// ([`igraph_add_edges`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_add_edges)
2058 /// with an attribute record list).
2059 ///
2060 /// Each record must be named and hold exactly `edges.len()` values.
2061 ///
2062 /// # Errors
2063 ///
2064 /// [`ErrorKind::InvalidVertexId`] for invalid endpoints,
2065 /// [`ErrorKind::InvalidValue`] for invalid records (see
2066 /// [`add_vertices_with_attributes`](Self::add_vertices_with_attributes)).
2067 ///
2068 /// ```
2069 /// use igraph::prelude::*;
2070 /// use igraph::attributes::AttributeRecord;
2071 ///
2072 /// let mut g = Graph::new(3, true);
2073 /// let w = AttributeRecord::numeric("weight", &[1.5, 2.5]).unwrap();
2074 /// g.add_edges_with_attributes(&[(0, 1), (1, 2)], &[w]).unwrap();
2075 /// assert_eq!(g.edge_attr_numeric("weight", 1).unwrap(), 2.5);
2076 /// ```
2077 pub fn add_edges_with_attributes(
2078 &mut self,
2079 edges: &[(VertexId, VertexId)],
2080 records: &[AttributeRecord],
2081 ) -> Result<()> {
2082 let flat: VectorInt = edges.iter().flat_map(|&(a, b)| [a, b]).collect();
2083 let list = RecordList::from_records(records)?;
2084 self.ensure_attr_storage()?;
2085 igraph_call!(igraph_add_edges(self, &flat, &list.0))
2086 }
2087
2088 // --- merging with attribute combinations ------------------------------------
2089
2090 /// Removes multi-edges and/or self-loops like [`Graph::simplify`],
2091 /// combining the attributes of the merged edges according to `edge_comb`
2092 /// ([`igraph_simplify`](https://igraph.org/c/html/latest/igraph-Operators.html#igraph_simplify)
2093 /// with an edge attribute combination).
2094 ///
2095 /// When multi-edges are removed, igraph rebuilds the graph: each group of
2096 /// parallel edges becomes one edge whose attributes are computed from the
2097 /// values of the group (a group of a single edge is combined too, e.g.
2098 /// [`Sum`](AttributeCombinationType::Sum) keeps its value). Attributes
2099 /// the combination maps to [`Ignore`](AttributeCombinationType::Ignore)
2100 /// or [`Default`](AttributeCombinationType::Default), and all of them for
2101 /// an empty combination, are dropped. The edge order may change.
2102 ///
2103 /// `edge_comb` is **not** used, and the remaining edges keep all their
2104 /// attributes, when nothing has to be merged: if `remove_multiple` is
2105 /// `false` (loops are then deleted in place), or if igraph already knows
2106 /// that the graph has no multi-edges (e.g. it was simplified before), or
2107 /// nothing at all is to be removed. Graph and vertex attributes are
2108 /// always kept. Without attributes enabled this is exactly
2109 /// [`Graph::simplify`].
2110 ///
2111 /// Time complexity: O(|V|+|E|), plus the cost of the combinations.
2112 ///
2113 /// # Errors
2114 ///
2115 /// [`ErrorKind::AttributeCombination`] if a combination does not apply to
2116 /// the type of an attribute (e.g. summing strings),
2117 /// [`ErrorKind::Unimplemented`] for the numeric median.
2118 ///
2119 /// # Examples
2120 ///
2121 /// ```
2122 /// use igraph::prelude::*;
2123 /// use igraph::attributes::{AttributeCombination, AttributeCombinationType as Comb};
2124 ///
2125 /// // Three calls between 0 and 1, one between 1 and 2.
2126 /// let mut calls = Graph::from_edges(&[(0, 1), (1, 2), (0, 1), (0, 1)], 3, false).unwrap();
2127 /// calls.set_edge_attr_numeric_values("minutes", &[5.0, 2.0, 1.0, 4.0]).unwrap();
2128 /// let comb = AttributeCombination::all(Comb::Sum).unwrap();
2129 /// calls.simplify_with_attributes(true, true, &comb).unwrap();
2130 /// assert_eq!(calls.edge_list(), vec![(0, 1), (1, 2)]);
2131 /// assert_eq!(calls.edge_attr_numeric_values("minutes", ..).unwrap(), vec![10.0, 2.0]);
2132 /// ```
2133 pub fn simplify_with_attributes(
2134 &mut self,
2135 remove_multiple: bool,
2136 remove_loops: bool,
2137 edge_comb: &AttributeCombination,
2138 ) -> Result<()> {
2139 igraph_call!(igraph_simplify(
2140 self,
2141 remove_multiple,
2142 remove_loops,
2143 edge_comb.as_ptr()
2144 ))
2145 }
2146
2147 /// Merges groups of vertices into single vertices like
2148 /// [`Graph::contract_vertices`], combining their attributes according to
2149 /// `vertex_comb` ([`igraph_contract_vertices`](https://igraph.org/c/html/latest/igraph-Operators.html#igraph_contract_vertices)
2150 /// with a vertex attribute combination).
2151 ///
2152 /// `mapping[v]` is the id of the vertex that `v` becomes; use consecutive
2153 /// ids starting at 0 to avoid creating isolated "orphan" vertices. No
2154 /// edge is removed (merged groups get self-loops and multi-edges: use
2155 /// [`simplify_with_attributes`](Self::simplify_with_attributes)
2156 /// afterwards), and edge and graph attributes are kept unchanged.
2157 ///
2158 /// Time complexity: O(|V|+|E|), plus the cost of the combinations.
2159 ///
2160 /// # Errors
2161 ///
2162 /// [`ErrorKind::InvalidValue`] if `mapping` does not have one entry per
2163 /// vertex or contains negative ids (or `i64::MAX`); the
2164 /// combination errors of
2165 /// [`simplify_with_attributes`](Self::simplify_with_attributes).
2166 ///
2167 /// # Examples
2168 ///
2169 /// ```
2170 /// use igraph::prelude::*;
2171 /// use igraph::attributes::{AttributeCombination, AttributeCombinationType as Comb};
2172 ///
2173 /// // Four towns in two provinces.
2174 /// let mut g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
2175 /// g.set_vertex_attr_numeric_values("population", &[10.0, 5.0, 7.0, 3.0]).unwrap();
2176 /// g.set_vertex_attr_str_values("name", &["Firenze", "Prato", "Pisa", "Lucca"]).unwrap();
2177 /// let comb = AttributeCombination::from_pairs(&[
2178 /// (Some("population"), Comb::Sum),
2179 /// (Some("name"), Comb::First),
2180 /// ]).unwrap();
2181 /// g.contract_vertices_with_attributes(&[0, 0, 1, 1], &comb).unwrap();
2182 /// assert_eq!(g.vertex_attr_numeric_values("population", ..).unwrap(), vec![15.0, 10.0]);
2183 /// assert_eq!(g.vertex_attr_str_values("name", ..).unwrap(), vec!["Firenze", "Pisa"]);
2184 /// assert_eq!(g.ecount(), 3); // two self-loops and the edge between the provinces
2185 /// ```
2186 pub fn contract_vertices_with_attributes(
2187 &mut self,
2188 mapping: &[VertexId],
2189 vertex_comb: &AttributeCombination,
2190 ) -> Result<()> {
2191 if mapping.len() != self.vcount() {
2192 return Err(Error::invalid(format!(
2193 "the mapping has {} entries but the graph has {} vertices",
2194 mapping.len(),
2195 self.vcount()
2196 )));
2197 }
2198 // Negative ids index out of bounds in C, and igraph computes the new
2199 // vertex count as `max + 1`, which overflows for `VertexId::MAX`.
2200 if let Some(&bad) = mapping.iter().find(|&&m| !(0..VertexId::MAX).contains(&m)) {
2201 return Err(Error::invalid(format!(
2202 "the mapping contains the invalid vertex id {bad}"
2203 )));
2204 }
2205 let m = VectorInt::view(mapping);
2206 igraph_call!(igraph_contract_vertices(
2207 self,
2208 m.as_ptr(),
2209 vertex_comb.as_ptr()
2210 ))
2211 }
2212
2213 /// Converts a directed graph to an undirected one like
2214 /// [`Graph::to_undirected`], combining the attributes of the directed
2215 /// edges that become a single undirected edge according to `edge_comb`
2216 /// ([`igraph_to_undirected`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_to_undirected)
2217 /// with an edge attribute combination).
2218 ///
2219 /// With [`ToUndirected::Collapse`] all the edges between a pair of
2220 /// vertices are merged; with [`ToUndirected::Mutual`] each mutual pair
2221 /// `u -> v`, `v -> u` is merged into one edge (non-mutual edges are lost,
2222 /// loops are kept); with [`ToUndirected::Each`] every edge is kept together
2223 /// with its attributes and `edge_comb` is not used. Graph and vertex
2224 /// attributes are kept. Undirected graphs are left unchanged.
2225 ///
2226 /// Time complexity: O(|V|+|E|), plus the cost of the combinations.
2227 ///
2228 /// # Errors
2229 ///
2230 /// The combination errors of
2231 /// [`simplify_with_attributes`](Self::simplify_with_attributes).
2232 ///
2233 /// # Examples
2234 ///
2235 /// ```
2236 /// use igraph::prelude::*;
2237 /// use igraph::attributes::{AttributeCombination, AttributeCombinationType as Comb};
2238 ///
2239 /// // Messages sent in both directions between 0 and 1, and from 1 to 2.
2240 /// let mut g = Graph::from_edges(&[(0, 1), (1, 0), (1, 2)], 3, true).unwrap();
2241 /// g.set_edge_attr_numeric_values("messages", &[3.0, 4.0, 1.0]).unwrap();
2242 /// let comb = AttributeCombination::all(Comb::Sum).unwrap();
2243 /// g.to_undirected_with_attributes(ToUndirected::Collapse, &comb).unwrap();
2244 /// assert_eq!(g.edge_list(), vec![(0, 1), (1, 2)]);
2245 /// assert_eq!(g.edge_attr_numeric_values("messages", ..).unwrap(), vec![7.0, 1.0]);
2246 /// ```
2247 pub fn to_undirected_with_attributes(
2248 &mut self,
2249 mode: ToUndirected,
2250 edge_comb: &AttributeCombination,
2251 ) -> Result<()> {
2252 igraph_call!(igraph_to_undirected(self, mode.into(), edge_comb.as_ptr()))
2253 }
2254}
2255
2256// ---------------------------------------------------------------------------
2257// Attribute records
2258// ---------------------------------------------------------------------------
2259
2260/// A named, typed vector of attribute values (`igraph_attribute_record_t`).
2261///
2262/// Attribute records are the unit of data exchanged between igraph and an
2263/// attribute handler; in Rust they are mostly useful to add vertices or edges
2264/// together with their attributes, see [`Graph::add_vertices_with_attributes`].
2265/// A record owns its values and frees them on [`Drop`]; [`Clone`] makes a
2266/// deep copy, default value included.
2267///
2268/// A record also has a *default value*, used to fill new slots when it is
2269/// [resized](Self::resize): `NaN`, `false` or `""` unless changed with
2270/// [`set_default`](Self::set_default).
2271///
2272/// ```
2273/// use igraph::attributes::{AttributeRecord, AttributeType, AttributeValue, AttributeValues};
2274///
2275/// let mut rec = AttributeRecord::numeric("score", &[1.0, 2.0]).unwrap();
2276/// rec.set_default(AttributeValue::Numeric(-1.0)).unwrap();
2277/// rec.resize(4).unwrap();
2278/// assert_eq!(rec.name(), Some("score"));
2279/// assert_eq!(rec.attribute_type(), AttributeType::Numeric);
2280/// assert_eq!(rec.values(), AttributeValues::Numeric(vec![1.0, 2.0, -1.0, -1.0]));
2281/// ```
2282pub type AttributeRecord = igraph_attribute_record_t;
2283
2284impl igraph_attribute_record_t {
2285 /// Creates an empty record with the given name and type
2286 /// ([`igraph_attribute_record_init`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_attribute_record_init)).
2287 ///
2288 /// An [`AttributeType::Unspecified`] record is untyped: it holds no
2289 /// values until [`set_type`](Self::set_type) gives it a type.
2290 ///
2291 /// Time complexity: O(1).
2292 ///
2293 /// # Errors
2294 ///
2295 /// [`ErrorKind::InvalidValue`] if `name` contains a NUL byte or the type
2296 /// is [`AttributeType::Object`] (checked in Rust: since igraph 1.0.1 the
2297 /// C library treats an unsupported type as a *fatal* error and aborts the
2298 /// process, while 1.0.0 returned an error).
2299 ///
2300 /// # Examples
2301 ///
2302 /// ```
2303 /// use igraph::attributes::{AttributeRecord, AttributeType};
2304 /// let mut rec = AttributeRecord::new("color", AttributeType::String).unwrap();
2305 /// assert!(rec.is_empty());
2306 /// rec.resize(2).unwrap();
2307 /// assert_eq!(rec.values().as_strings().unwrap(), ["", ""]);
2308 /// assert!(AttributeRecord::new("o", AttributeType::Object).is_err());
2309 /// ```
2310 pub fn new(name: &str, attribute_type: AttributeType) -> Result<Self> {
2311 let c = c_string("attribute name", name)?;
2312 if attribute_type == AttributeType::Object {
2313 return Err(Error::invalid("object attributes are not supported"));
2314 }
2315 // SAFETY: `c` is a valid C string and the type is supported.
2316 unsafe { Self::init_raw(c.as_ptr(), attribute_type.into()) }
2317 }
2318
2319 /// `igraph_attribute_record_init` into a new record.
2320 ///
2321 /// # Safety
2322 ///
2323 /// `name` must be NULL or a valid C string, and `type_` must be
2324 /// `UNSPECIFIED`, `NUMERIC`, `BOOLEAN` or `STRING` (igraph 1.0.1 aborts
2325 /// the process on any other type).
2326 unsafe fn init_raw(name: *const c_char, type_: igraph_attribute_type_t) -> Result<Self> {
2327 let mut raw = MaybeUninit::<Self>::uninit();
2328 // On failure the record must *not* be destroyed: igraph 1.0.1 has
2329 // already released everything it allocated (the name through the
2330 // "finally" stack, which leaves `name` dangling), so destroying it
2331 // would free the name twice.
2332 igraph_call!(igraph_attribute_record_init(raw.as_mut_ptr(), name, type_))?;
2333 // SAFETY: `init` succeeded, so every field is initialized.
2334 Ok(unsafe { raw.assume_init() })
2335 }
2336
2337 /// Creates a numeric record holding `values`.
2338 pub fn numeric(name: &str, values: &[f64]) -> Result<Self> {
2339 let mut rec = Self::new(name, AttributeType::Numeric)?;
2340 let v = Vector::view(values);
2341 igraph_call!(igraph_vector_update(
2342 *unsafe { rec.value.as_vector.as_mut() },
2343 v.as_ptr()
2344 ))?;
2345 Ok(rec)
2346 }
2347
2348 /// Creates a boolean record holding `values`.
2349 pub fn boolean(name: &str, values: &[bool]) -> Result<Self> {
2350 let mut rec = Self::new(name, AttributeType::Boolean)?;
2351 let v = VectorBool::view(values);
2352 igraph_call!(igraph_vector_bool_update(
2353 *unsafe { rec.value.as_vector_bool.as_mut() },
2354 v.as_ptr()
2355 ))?;
2356 Ok(rec)
2357 }
2358
2359 /// Creates a string record holding `values`.
2360 ///
2361 /// # Errors
2362 ///
2363 /// [`ErrorKind::InvalidValue`] if a string contains a NUL byte.
2364 pub fn string<S: AsRef<str>>(name: &str, values: &[S]) -> Result<Self> {
2365 let sv = to_strvector(values)?;
2366 let mut rec = Self::new(name, AttributeType::String)?;
2367 igraph_call!(igraph_strvector_update(
2368 *unsafe { rec.value.as_strvector.as_mut() },
2369 &sv
2370 ))?;
2371 Ok(rec)
2372 }
2373
2374 /// Creates a record of the type of `values`.
2375 pub fn from_values(name: &str, values: impl Into<AttributeValues>) -> Result<Self> {
2376 match values.into() {
2377 AttributeValues::Numeric(v) => Self::numeric(name, &v),
2378 AttributeValues::Boolean(v) => Self::boolean(name, &v),
2379 AttributeValues::String(v) => Self::string(name, &v),
2380 }
2381 }
2382
2383 /// The name of the attribute (`None` if unset or not UTF-8).
2384 pub fn name(&self) -> Option<&str> {
2385 if self.name.is_null() {
2386 None
2387 } else {
2388 unsafe { CStr::from_ptr(self.name) }.to_str().ok()
2389 }
2390 }
2391
2392 /// Renames the record
2393 /// ([`igraph_attribute_record_set_name`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_attribute_record_set_name)).
2394 pub fn set_name(&mut self, name: &str) -> Result<()> {
2395 let c = c_string("attribute name", name)?;
2396 igraph_call!(igraph_attribute_record_set_name(self, c.as_ptr()))
2397 }
2398
2399 /// The type of the values.
2400 pub fn attribute_type(&self) -> AttributeType {
2401 AttributeType::try_from(self.type_).unwrap_or(AttributeType::Object)
2402 }
2403
2404 /// Changes the type of the record
2405 /// ([`igraph_attribute_record_set_type`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_attribute_record_set_type)).
2406 ///
2407 /// When the type changes, the values are discarded and the record becomes
2408 /// empty; setting the same type is a no-op.
2409 ///
2410 /// # Errors
2411 ///
2412 /// [`ErrorKind::InvalidValue`] for [`AttributeType::Unspecified`] and
2413 /// [`AttributeType::Object`], checked in Rust: igraph 1.0.1 aborts the
2414 /// process on these (a fatal error in `igraph_attribute_record_set_type`;
2415 /// 1.0.0 returned `IGRAPH_EINVAL`).
2416 ///
2417 /// # Examples
2418 ///
2419 /// ```
2420 /// use igraph::attributes::{AttributeRecord, AttributeType};
2421 /// let mut rec = AttributeRecord::numeric("x", &[1.0, 2.0]).unwrap();
2422 /// rec.set_type(AttributeType::Numeric).unwrap(); // same type: no-op
2423 /// assert_eq!(rec.len(), 2);
2424 /// rec.set_type(AttributeType::Boolean).unwrap(); // new type: values dropped
2425 /// assert!(rec.is_empty());
2426 /// assert!(rec.set_type(AttributeType::Unspecified).is_err());
2427 /// ```
2428 pub fn set_type(&mut self, attribute_type: AttributeType) -> Result<()> {
2429 if matches!(
2430 attribute_type,
2431 AttributeType::Unspecified | AttributeType::Object
2432 ) {
2433 return Err(Error::invalid(format!(
2434 "cannot set the record type to {attribute_type}"
2435 )));
2436 }
2437 igraph_call!(igraph_attribute_record_set_type(
2438 self,
2439 attribute_type.into()
2440 ))
2441 }
2442
2443 /// Checks that the record has the expected type
2444 /// ([`igraph_attribute_record_check_type`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_attribute_record_check_type)).
2445 ///
2446 /// # Errors
2447 ///
2448 /// [`ErrorKind::InvalidValue`] if the types differ.
2449 pub fn check_type(&self, expected: AttributeType) -> Result<()> {
2450 igraph_call!(igraph_attribute_record_check_type(self, expected.into()))
2451 }
2452
2453 /// Number of values
2454 /// ([`igraph_attribute_record_size`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_attribute_record_size)).
2455 pub fn len(&self) -> usize {
2456 match self.attribute_type() {
2457 AttributeType::Numeric | AttributeType::Boolean | AttributeType::String => unsafe {
2458 igraph_attribute_record_size(self) as usize
2459 },
2460 _ => 0,
2461 }
2462 }
2463
2464 /// Whether the record holds no values.
2465 pub fn is_empty(&self) -> bool {
2466 self.len() == 0
2467 }
2468
2469 /// Resizes the value vector, filling new slots with the default value
2470 /// ([`igraph_attribute_record_resize`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_attribute_record_resize)).
2471 ///
2472 /// # Errors
2473 ///
2474 /// [`ErrorKind::InvalidValue`] if the record has no type yet.
2475 pub fn resize(&mut self, len: usize) -> Result<()> {
2476 if !matches!(
2477 self.attribute_type(),
2478 AttributeType::Numeric | AttributeType::Boolean | AttributeType::String
2479 ) {
2480 return Err(Error::invalid("the attribute record has no type yet"));
2481 }
2482 let len = igraph_int_t::try_from(len)
2483 .map_err(|_| Error::invalid(format!("record length {len} is too large")))?;
2484 igraph_call!(igraph_attribute_record_resize(self, len))
2485 }
2486
2487 /// Sets the value used to fill new slots on [`resize`](Self::resize)
2488 /// ([`igraph_attribute_record_set_default_numeric`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_attribute_record_set_default_numeric),
2489 /// [`_boolean`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_attribute_record_set_default_boolean),
2490 /// [`_string`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_attribute_record_set_default_string)).
2491 ///
2492 /// # Errors
2493 ///
2494 /// [`ErrorKind::InvalidValue`] if the value's type is not the record's type.
2495 pub fn set_default(&mut self, value: impl Into<AttributeValue>) -> Result<()> {
2496 let value = value.into();
2497 if value.attribute_type() != self.attribute_type() {
2498 return Err(Error::invalid(format!(
2499 "a {} default value does not fit a {} attribute record",
2500 value.attribute_type(),
2501 self.attribute_type()
2502 )));
2503 }
2504 match value {
2505 AttributeValue::Numeric(x) => {
2506 igraph_call!(igraph_attribute_record_set_default_numeric(self, x))
2507 }
2508 AttributeValue::Boolean(b) => {
2509 igraph_call!(igraph_attribute_record_set_default_boolean(self, b))
2510 }
2511 AttributeValue::String(s) => {
2512 let c = c_string("string value", &s)?;
2513 igraph_call!(igraph_attribute_record_set_default_string(self, c.as_ptr()))
2514 }
2515 }
2516 }
2517
2518 /// The value used to fill new slots on [`resize`](Self::resize), see
2519 /// [`set_default`](Self::set_default); `None` for an untyped record.
2520 ///
2521 /// Unless changed, it is `NaN`, `false` or `""` depending on the type.
2522 ///
2523 /// ```
2524 /// use igraph::attributes::{AttributeRecord, AttributeValue};
2525 /// let mut rec = AttributeRecord::string("city", &["Firenze"]).unwrap();
2526 /// assert_eq!(rec.default_value(), Some(AttributeValue::String(String::new())));
2527 /// rec.set_default("unknown").unwrap();
2528 /// assert_eq!(rec.default_value(), Some(AttributeValue::String("unknown".into())));
2529 /// ```
2530 pub fn default_value(&self) -> Option<AttributeValue> {
2531 // SAFETY: the active union member is the one matching `type_`.
2532 unsafe {
2533 match self.attribute_type() {
2534 AttributeType::Numeric => Some(AttributeValue::Numeric(
2535 *self.default_value.numeric.as_ref(),
2536 )),
2537 AttributeType::Boolean => Some(AttributeValue::Boolean(
2538 *self.default_value.boolean.as_ref(),
2539 )),
2540 AttributeType::String => Some(AttributeValue::String(lossy(
2541 *self.default_value.string.as_ref(),
2542 ))),
2543 _ => None,
2544 }
2545 }
2546 }
2547
2548 /// A copy of the values (an empty [`AttributeValues::Numeric`] for an
2549 /// untyped record).
2550 pub fn values(&self) -> AttributeValues {
2551 unsafe {
2552 match self.attribute_type() {
2553 AttributeType::Numeric => {
2554 AttributeValues::Numeric((**self.value.as_vector.as_ref()).to_vec())
2555 }
2556 AttributeType::Boolean => {
2557 AttributeValues::Boolean((**self.value.as_vector_bool.as_ref()).to_vec())
2558 }
2559 AttributeType::String => {
2560 AttributeValues::String((**self.value.as_strvector.as_ref()).to_vec())
2561 }
2562 _ => AttributeValues::Numeric(Vec::new()),
2563 }
2564 }
2565 }
2566}
2567
2568impl Drop for igraph_attribute_record_t {
2569 /// Frees the name and the values (`igraph_attribute_record_destroy`).
2570 fn drop(&mut self) {
2571 unsafe { igraph_attribute_record_destroy(self) }
2572 }
2573}
2574
2575impl Clone for igraph_attribute_record_t {
2576 /// Deep copy of the name, type, values and [default
2577 /// value](Self::default_value).
2578 ///
2579 /// It does not use `igraph_attribute_record_init_copy`: that function
2580 /// does not copy the default value, and when it fails its partially
2581 /// initialized result can be neither destroyed nor leaked safely.
2582 ///
2583 /// # Panics
2584 ///
2585 /// If igraph fails to allocate the copy.
2586 fn clone(&self) -> Self {
2587 let ty = self.attribute_type();
2588 assert!(
2589 ty != AttributeType::Object,
2590 "attribute records of unsupported types cannot be cloned"
2591 );
2592 // SAFETY: `self.name` is NULL or a valid C string owned by `self`,
2593 // and the type is supported (checked above).
2594 let mut copy = unsafe { Self::init_raw(self.name, ty.into()) }
2595 .expect("igraph failed to copy an attribute record");
2596 // From here on `copy` is a valid record, freed by `Drop` on panic.
2597 // SAFETY: both records have type `ty`, so the matching union
2598 // members are the active ones and point to initialized vectors.
2599 igraph_call!(match ty {
2600 AttributeType::Numeric => igraph_vector_update(
2601 *copy.value.as_vector.as_mut(),
2602 *self.value.as_vector.as_ref(),
2603 ),
2604 AttributeType::Boolean => igraph_vector_bool_update(
2605 *copy.value.as_vector_bool.as_mut(),
2606 *self.value.as_vector_bool.as_ref(),
2607 ),
2608 AttributeType::String => igraph_strvector_update(
2609 *copy.value.as_strvector.as_mut(),
2610 *self.value.as_strvector.as_ref(),
2611 ),
2612 _ => igraph_error_type_t_IGRAPH_SUCCESS,
2613 })
2614 .expect("igraph failed to copy an attribute record");
2615 if let Some(default) = self.default_value() {
2616 copy.set_default(default)
2617 .expect("igraph failed to copy the default value of an attribute record");
2618 }
2619 copy
2620 }
2621}
2622
2623impl PartialEq for igraph_attribute_record_t {
2624 /// Records are equal when their names, types, values and default values
2625 /// are. Records are compared as data: a `NaN` number equals another
2626 /// `NaN` (the default value of a numeric record is `NaN` unless changed,
2627 /// so with `f64` semantics a numeric record would never equal its clone).
2628 fn eq(&self, other: &Self) -> bool {
2629 fn same(a: f64, b: f64) -> bool {
2630 a == b || (a.is_nan() && b.is_nan())
2631 }
2632 let same_values = match (self.values(), other.values()) {
2633 (AttributeValues::Numeric(a), AttributeValues::Numeric(b)) => {
2634 a.len() == b.len() && a.iter().zip(&b).all(|(&x, &y)| same(x, y))
2635 }
2636 (a, b) => a == b,
2637 };
2638 let same_default = match (self.default_value(), other.default_value()) {
2639 (Some(AttributeValue::Numeric(a)), Some(AttributeValue::Numeric(b))) => same(a, b),
2640 (a, b) => a == b,
2641 };
2642 self.name() == other.name()
2643 && self.attribute_type() == other.attribute_type()
2644 && same_values
2645 && same_default
2646 }
2647}
2648
2649impl fmt::Debug for igraph_attribute_record_t {
2650 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2651 f.debug_struct("AttributeRecord")
2652 .field("name", &self.name())
2653 .field("type", &self.attribute_type())
2654 .field("values", &self.values())
2655 .field("default", &self.default_value())
2656 .finish()
2657 }
2658}
2659
2660// A record exclusively owns its heap data.
2661unsafe impl Send for igraph_attribute_record_t {}
2662unsafe impl Sync for igraph_attribute_record_t {}
2663
2664// ---------------------------------------------------------------------------
2665// Attribute combinations
2666// ---------------------------------------------------------------------------
2667
2668/// Signature of a numeric combination function of the C attribute handler:
2669/// `input` holds the values of the merged elements, the result goes to `output`.
2670pub type NumericCombineFn = unsafe extern "C" fn(
2671 input: *const igraph_vector_t,
2672 output: *mut igraph_real_t,
2673) -> igraph_error_t;
2674
2675/// Signature of a boolean combination function of the C attribute handler.
2676pub type BooleanCombineFn = unsafe extern "C" fn(
2677 input: *const igraph_vector_bool_t,
2678 output: *mut igraph_bool_t,
2679) -> igraph_error_t;
2680
2681/// A user supplied C function combining attribute values, see
2682/// [`AttributeCombination::add_function`].
2683#[derive(Debug, Clone, Copy)]
2684pub enum CombineFunction {
2685 /// For numeric attributes.
2686 Numeric(NumericCombineFn),
2687 /// For boolean attributes.
2688 Boolean(BooleanCombineFn),
2689}
2690
2691/// How to combine attributes when vertices or edges are merged
2692/// (`igraph_attribute_combination_t`).
2693///
2694/// It maps attribute names to an [`AttributeCombinationType`]; the entry
2695/// with no name (`None`) is the default for every attribute not listed. An
2696/// empty combination drops all the attributes of merged elements.
2697///
2698/// Pass it to [`Graph::simplify_with_attributes`],
2699/// [`Graph::contract_vertices_with_attributes`] or
2700/// [`Graph::to_undirected_with_attributes`], or to other igraph functions
2701/// merging elements through the raw FFI with [`as_ptr`](Self::as_ptr).
2702///
2703/// ```
2704/// use igraph::attributes::{AttributeCombination, AttributeCombinationType as Comb};
2705///
2706/// let comb = AttributeCombination::from_pairs(&[
2707/// (Some("weight"), Comb::Sum),
2708/// (Some("name"), Comb::First),
2709/// (None, Comb::Ignore),
2710/// ]).unwrap();
2711/// assert_eq!(comb.query(Some("weight")).unwrap(), Comb::Sum);
2712/// assert_eq!(comb.query(Some("color")).unwrap(), Comb::Ignore); // falls back to the default
2713///
2714/// // Merge the two parallel edges: weights are summed, other attributes dropped.
2715/// use igraph::prelude::*;
2716/// let mut g = Graph::from_edges(&[(0, 1), (0, 1)], 2, false).unwrap();
2717/// g.set_edge_attr_numeric_values("weight", &[1.5, 2.0]).unwrap();
2718/// g.set_edge_attr_str_values("color", &["red", "blue"]).unwrap();
2719/// g.simplify_with_attributes(true, true, &comb).unwrap();
2720/// assert_eq!(g.edge_attr_numeric("weight", 0).unwrap(), 3.5);
2721/// assert!(!g.has_attribute(igraph::attributes::AttributeKind::Edge, "color"));
2722/// ```
2723pub type AttributeCombination = igraph_attribute_combination_t;
2724
2725impl igraph_attribute_combination_t {
2726 /// Creates an empty combination list
2727 /// ([`igraph_attribute_combination_init`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_attribute_combination_init)).
2728 ///
2729 /// # Panics
2730 ///
2731 /// If igraph fails to allocate it.
2732 pub fn new() -> Self {
2733 ensure_init();
2734 let mut raw = MaybeUninit::<Self>::uninit();
2735 crate::error::check(unsafe { igraph_attribute_combination_init(raw.as_mut_ptr()) })
2736 .expect("igraph failed to allocate an attribute combination");
2737 unsafe { raw.assume_init() }
2738 }
2739
2740 /// Creates a combination list from `(name, type)` pairs, `None` naming the
2741 /// default entry (the Rust counterpart of the variadic
2742 /// [`igraph_attribute_combination`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_attribute_combination)).
2743 ///
2744 /// # Errors
2745 ///
2746 /// As [`add`](Self::add).
2747 pub fn from_pairs(pairs: &[(Option<&str>, AttributeCombinationType)]) -> Result<Self> {
2748 let mut comb = Self::new();
2749 for &(name, kind) in pairs {
2750 comb.add(name, kind)?;
2751 }
2752 Ok(comb)
2753 }
2754
2755 /// A combination applying `kind` to every attribute (a single default
2756 /// entry).
2757 ///
2758 /// # Errors
2759 ///
2760 /// As [`add`](Self::add).
2761 ///
2762 /// ```
2763 /// use igraph::attributes::{AttributeCombination, AttributeCombinationType as Comb};
2764 /// let comb = AttributeCombination::all(Comb::Max).unwrap();
2765 /// assert_eq!(comb.query(None).unwrap(), Comb::Max);
2766 /// assert_eq!(comb.query(Some("anything")).unwrap(), Comb::Max);
2767 /// ```
2768 pub fn all(kind: AttributeCombinationType) -> Result<Self> {
2769 Self::from_pairs(&[(None, kind)])
2770 }
2771
2772 /// Adds or replaces the entry of an attribute (`None`: the default entry)
2773 /// ([`igraph_attribute_combination_add`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_attribute_combination_add)).
2774 ///
2775 /// # Errors
2776 ///
2777 /// [`ErrorKind::InvalidValue`] for [`AttributeCombinationType::Function`]
2778 /// (use [`add_function`](Self::add_function)) or a name containing a NUL byte.
2779 pub fn add(&mut self, name: Option<&str>, kind: AttributeCombinationType) -> Result<()> {
2780 if kind == AttributeCombinationType::Function {
2781 return Err(Error::invalid(
2782 "use `add_function` to combine attributes with a function",
2783 ));
2784 }
2785 let c = name.map(|n| c_string("attribute name", n)).transpose()?;
2786 let p = c.as_ref().map_or(ptr::null(), |c| c.as_ptr());
2787 igraph_call!(igraph_attribute_combination_add(self, p, kind.into(), None))
2788 }
2789
2790 /// Adds or replaces the entry of an attribute with a user supplied C
2791 /// function ([`igraph_attribute_combination_add`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_attribute_combination_add)
2792 /// with `IGRAPH_ATTRIBUTE_COMBINE_FUNCTION`).
2793 ///
2794 /// For each merged vertex or edge, the function receives the values of the
2795 /// merged elements and writes the combined value.
2796 ///
2797 /// # Safety
2798 ///
2799 /// The C attribute handler chooses the calling convention from the *type
2800 /// of the attribute* at combination time: the function must match it, i.e.
2801 /// a [`CombineFunction::Numeric`] may only be registered for numeric
2802 /// attributes and a [`CombineFunction::Boolean`] for boolean ones
2803 /// (registering a default entry with `name = None` requires *every*
2804 /// combined attribute to have that type). The function must not unwind.
2805 /// String functions are not supported by this wrapper (igraph would need to
2806 /// free a string allocated by Rust).
2807 ///
2808 /// ```
2809 /// use igraph::{ffi, attributes::{AttributeCombination, CombineFunction}};
2810 ///
2811 /// unsafe extern "C" fn count(
2812 /// input: *const ffi::igraph_vector_t,
2813 /// output: *mut f64,
2814 /// ) -> ffi::igraph_error_t {
2815 /// unsafe { *output = (&*input).len() as f64 };
2816 /// ffi::igraph_error_type_t_IGRAPH_SUCCESS
2817 /// }
2818 ///
2819 /// let mut comb = AttributeCombination::new();
2820 /// // SAFETY: "multiplicity" will be a numeric attribute.
2821 /// unsafe { comb.add_function(Some("multiplicity"), CombineFunction::Numeric(count)) }.unwrap();
2822 /// ```
2823 pub unsafe fn add_function(&mut self, name: Option<&str>, func: CombineFunction) -> Result<()> {
2824 let c = name.map(|n| c_string("attribute name", n)).transpose()?;
2825 let p = c.as_ref().map_or(ptr::null(), |c| c.as_ptr());
2826 // SAFETY: igraph stores the pointer type-erased and casts it back to the
2827 // signature matching the attribute type (the caller's responsibility).
2828 let erased: unsafe extern "C" fn() = unsafe {
2829 match func {
2830 CombineFunction::Numeric(f) => {
2831 std::mem::transmute::<NumericCombineFn, unsafe extern "C" fn()>(f)
2832 }
2833 CombineFunction::Boolean(f) => {
2834 std::mem::transmute::<BooleanCombineFn, unsafe extern "C" fn()>(f)
2835 }
2836 }
2837 };
2838 igraph_call!(igraph_attribute_combination_add(
2839 self,
2840 p,
2841 AttributeCombinationType::Function.into(),
2842 Some(erased)
2843 ))
2844 }
2845
2846 /// Removes the entry of an attribute (`None`: the default entry); missing
2847 /// entries are ignored
2848 /// ([`igraph_attribute_combination_remove`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_attribute_combination_remove)).
2849 pub fn remove(&mut self, name: Option<&str>) -> Result<()> {
2850 let c = name.map(|n| c_string("attribute name", n)).transpose()?;
2851 let p = c.as_ref().map_or(ptr::null(), |c| c.as_ptr());
2852 igraph_call!(igraph_attribute_combination_remove(self, p))
2853 }
2854
2855 /// How the attribute `name` will be combined
2856 /// ([`igraph_attribute_combination_query`](https://igraph.org/c/html/latest/igraph-Attributes.html#igraph_attribute_combination_query)).
2857 ///
2858 /// Falls back to the default entry, and to
2859 /// [`AttributeCombinationType::Default`] when there is none.
2860 pub fn query(&self, name: Option<&str>) -> Result<AttributeCombinationType> {
2861 let c = name.map(|n| c_string("attribute name", n)).transpose()?;
2862 let p = c.as_ref().map_or(ptr::null(), |c| c.as_ptr());
2863 let mut kind = igraph_attribute_combination_type_t_IGRAPH_ATTRIBUTE_COMBINE_DEFAULT;
2864 let mut func: igraph_function_pointer_t = None;
2865 igraph_call!(igraph_attribute_combination_query(
2866 self, p, &mut kind, &mut func
2867 ))?;
2868 AttributeCombinationType::try_from(kind)
2869 }
2870
2871 /// Raw pointer for the igraph functions taking a
2872 /// `const igraph_attribute_combination_t *`.
2873 pub fn as_ptr(&self) -> *const igraph_attribute_combination_t {
2874 self
2875 }
2876}
2877
2878impl Default for igraph_attribute_combination_t {
2879 fn default() -> Self {
2880 Self::new()
2881 }
2882}
2883
2884impl Drop for igraph_attribute_combination_t {
2885 /// Frees the list (`igraph_attribute_combination_destroy`).
2886 fn drop(&mut self) {
2887 if !self.list.stor_begin.is_null() {
2888 unsafe { igraph_attribute_combination_destroy(self) };
2889 self.list.stor_begin = ptr::null_mut();
2890 }
2891 }
2892}
2893
2894// The list exclusively owns its records.
2895unsafe impl Send for igraph_attribute_combination_t {}