1use std::{
2 collections::BTreeMap,
3 fmt::{Display, Write as FmtWrite},
4 io::{Result, Write},
5 ops::Index,
6};
7
8use super::Visibility;
9use crate::{
10 analysis::{
11 self,
12 general::StatusedTypeId,
13 imports::{ImportConditions, Imports},
14 namespaces,
15 special_functions::TraitInfo,
16 },
17 config::{derives::Derive, Config},
18 env::Env,
19 gir_version::VERSION,
20 library::TypeId,
21 nameutil::use_glib_type,
22 version::Version,
23 writer::primitives::tabs,
24};
25
26pub fn start_comments(w: &mut dyn Write, conf: &Config) -> Result<()> {
27 if conf.single_version_file.is_some() {
28 start_comments_no_version(w, conf)
29 } else {
30 single_version_file(w, conf, "// ")?;
31 writeln!(w, "// DO NOT EDIT")
32 }
33}
34
35pub fn start_comments_no_version(w: &mut dyn Write, conf: &Config) -> Result<()> {
36 writeln!(
37 w,
38 "// This file was generated by gir (https://github.com/gtk-rs/gir)"
39 )?;
40 write!(
41 w,
42 "{}",
43 conf.girs_version
44 .iter()
45 .fold(String::new(), |mut output, info| {
46 writeln!(
47 output,
48 "// from {}{}",
49 info.gir_dir.display(),
50 info.get_repository_url()
51 .map_or_else(String::new, |url| format!(" ({url})")),
52 )
53 .unwrap();
54 output
55 })
56 )?;
57 writeln!(w, "// DO NOT EDIT")?;
58 Ok(())
59}
60
61pub fn single_version_file(w: &mut dyn Write, conf: &Config, prefix: &str) -> Result<()> {
62 write!(
63 w,
64 "{}Generated by gir (https://github.com/gtk-rs/gir @ {})
65{}",
66 prefix,
67 VERSION,
68 conf.girs_version
69 .iter()
70 .fold(String::new(), |mut output, info| {
71 match (info.get_repository_url(), info.get_hash()) {
72 (Some(url), Some(hash)) => writeln!(
73 output,
74 "{}from {} ({} @ {})",
75 prefix,
76 info.gir_dir.display(),
77 url,
78 hash,
79 ),
80 (None, Some(hash)) => {
81 writeln!(
82 output,
83 "{}from {} (@ {})",
84 prefix,
85 info.gir_dir.display(),
86 hash,
87 )
88 }
89 _ => writeln!(output, "{}from {}", prefix, info.gir_dir.display()),
90 }
91 .unwrap();
92 output
93 }),
94 )
95}
96
97pub fn uses(
98 w: &mut dyn Write,
99 env: &Env,
100 imports: &Imports,
101 outer_version: Option<Version>,
102) -> Result<()> {
103 writeln!(w)?;
104
105 let mut grouped_imports: BTreeMap<(&str, Option<ImportConditions>), Vec<&str>> =
106 BTreeMap::new();
107
108 for (name, scope) in imports.iter() {
109 let (crate_name, import) = name.split_once("::").unwrap();
110 let mut scope = scope.clone();
111
112 scope.version = Version::if_stricter_than(scope.version, outer_version);
114 let to_compare_with = env.config.min_required_version(env, None);
115 scope.version = match (scope.version, to_compare_with) {
116 (Some(v), Some(to_compare_v)) => {
117 if v > to_compare_v {
118 scope.version
119 } else {
120 None
121 }
122 }
123 (Some(_), _) => scope.version,
124 _ => None,
125 };
126
127 let key = if scope.constraints.is_empty() && scope.version.is_none() {
128 (crate_name, None)
129 } else {
130 (crate_name, Some(scope))
131 };
132 grouped_imports
133 .entry(key)
134 .and_modify(|entry| entry.push(import))
135 .or_insert_with(|| vec![import]);
136 }
137
138 for ((crate_name, scope), names) in grouped_imports.iter() {
139 if !scope.is_none() {
140 let scope = scope.as_ref().unwrap();
141 if !scope.constraints.is_empty() {
142 if scope.constraints.len() == 1 {
143 writeln!(w, "#[cfg({})]", scope.constraints[0])?;
144 } else {
145 writeln!(w, "#[cfg(any({}))]", scope.constraints.join(", "))?;
146 }
147 writeln!(
148 w,
149 "#[cfg_attr(docsrs, doc(cfg({})))]",
150 scope.constraints.join(", ")
151 )?;
152 }
153 version_condition(w, env, None, scope.version, false, 0)?;
154 }
155 writeln!(w, "use {crate_name}::{{{}}};", names.join(","))?;
156 }
157
158 Ok(())
159}
160
161fn format_parent_name(env: &Env, p: &StatusedTypeId) -> String {
162 if p.type_id.ns_id == namespaces::MAIN {
163 p.name.clone()
164 } else {
165 format!(
166 "{krate}::{name}",
167 krate = env.namespaces[p.type_id.ns_id].crate_name,
168 name = p.name,
169 )
170 }
171}
172
173pub fn define_fundamental_type(
174 w: &mut dyn Write,
175 env: &Env,
176 type_name: &str,
177 glib_name: &str,
178 glib_func_name: &str,
179 ref_func: Option<&str>,
180 unref_func: Option<&str>,
181 parents: &[StatusedTypeId],
182 visibility: Visibility,
183 type_id: TypeId,
184) -> Result<()> {
185 let sys_crate_name = env.sys_crate_import(type_id);
186 writeln!(w, "{} {{", use_glib_type(env, "wrapper!"))?;
187 doc_alias(w, glib_name, "", 1)?;
188 external_doc_link(
189 w,
190 env.config.external_docs_url.as_deref(),
191 type_name,
192 &visibility,
193 1,
194 )?;
195 writeln!(
196 w,
197 "\t{visibility} struct {type_name}(Shared<{sys_crate_name}::{glib_name}>);"
198 )?;
199 writeln!(w)?;
200
201 writeln!(w, "\tmatch fn {{")?;
202 let (ref_fn, unref_fn, ptr, ffi_crate_name) = if parents.is_empty() {
203 (
205 ref_func.unwrap().to_owned(),
206 unref_func.unwrap().to_owned(),
207 "ptr".to_owned(),
208 sys_crate_name.to_owned(),
209 )
210 } else {
211 let (ref_fn, unref_fn, ptr, ffi_crate_name) = parents
212 .iter()
213 .find_map(|p| {
214 use crate::library::*;
215 let type_ = env.library.type_(p.type_id);
216 let parent_sys_crate_name = env.sys_crate_import(p.type_id);
217 match type_ {
218 Type::Class(class) => Some((
219 class.ref_fn.as_ref().unwrap().clone(),
220 class.unref_fn.as_ref().unwrap().clone(),
221 format!(
222 "ptr as *mut {}::{}",
223 parent_sys_crate_name,
224 class.c_type.clone()
225 ),
226 parent_sys_crate_name,
227 )),
228 _ => None,
229 }
230 })
231 .unwrap();
232 (ref_fn, unref_fn, ptr, ffi_crate_name)
234 };
235
236 writeln!(w, "\t\tref => |ptr| {ffi_crate_name}::{ref_fn}({ptr}),")?;
237 writeln!(w, "\t\tunref => |ptr| {ffi_crate_name}::{unref_fn}({ptr}),")?;
238
239 writeln!(w, "\t}}")?;
240 writeln!(w, "}}")?;
241
242 writeln!(w, "\n")?;
245 writeln!(w, "impl StaticType for {} {{", type_name)?;
246 writeln!(w, "\tfn static_type() -> {} {{", use_glib_type(env, "Type"))?;
247 writeln!(
248 w,
249 "\t\t unsafe {{ from_glib({sys_crate_name}::{glib_func_name}()) }}"
250 )?;
251 writeln!(w, "\t}}")?;
252 writeln!(w, "}}")?;
253 Ok(())
254}
255
256pub fn define_object_type(
257 w: &mut dyn Write,
258 env: &Env,
259 type_name: &str,
260 glib_name: &str,
261 glib_class_name: Option<&str>,
262 glib_func_name: &str,
263 is_interface: bool,
264 parents: &[StatusedTypeId],
265 visibility: Visibility,
266 type_id: TypeId,
267) -> Result<()> {
268 let sys_crate_name = env.sys_crate_import(type_id);
269 let class_name = {
270 if let Some(s) = glib_class_name {
271 format!(", {sys_crate_name}::{s}")
272 } else {
273 String::new()
274 }
275 };
276
277 let kind_name = if is_interface { "Interface" } else { "Object" };
278
279 let parents: Vec<StatusedTypeId> = parents
280 .iter()
281 .filter(|p| !p.status.ignored())
282 .cloned()
283 .collect();
284
285 writeln!(w, "{} {{", use_glib_type(env, "wrapper!"))?;
286 doc_alias(w, glib_name, "", 1)?;
287 external_doc_link(
288 w,
289 env.config.external_docs_url.as_deref(),
290 type_name,
291 &visibility,
292 1,
293 )?;
294 if parents.is_empty() {
295 writeln!(
296 w,
297 "\t{visibility} struct {type_name}({kind_name}<{sys_crate_name}::{glib_name}{class_name}>);"
298 )?;
299 } else if is_interface {
300 let prerequisites: Vec<String> =
301 parents.iter().map(|p| format_parent_name(env, p)).collect();
302
303 writeln!(
304 w,
305 "\t{} struct {}(Interface<{}::{}{}>) @requires {};",
306 visibility,
307 type_name,
308 sys_crate_name,
309 glib_name,
310 class_name,
311 prerequisites.join(", ")
312 )?;
313 } else {
314 let interfaces: Vec<String> = parents
315 .iter()
316 .filter(|p| {
317 use crate::library::*;
318
319 matches!(
320 *env.library.type_(p.type_id),
321 Type::Interface { .. } if !p.status.ignored()
322 )
323 })
324 .map(|p| format_parent_name(env, p))
325 .collect();
326
327 let parents: Vec<String> = parents
328 .iter()
329 .filter(|p| {
330 use crate::library::*;
331
332 matches!(
333 *env.library.type_(p.type_id),
334 Type::Class { .. } if !p.status.ignored()
335 )
336 })
337 .map(|p| format_parent_name(env, p))
338 .collect();
339
340 let mut parents_string = String::new();
341 if !parents.is_empty() {
342 parents_string.push_str(format!(" @extends {}", parents.join(", ")).as_str());
343 }
344
345 if !interfaces.is_empty() {
346 if !parents.is_empty() {
347 parents_string.push(',');
348 }
349 parents_string.push_str(format!(" @implements {}", interfaces.join(", ")).as_str());
350 }
351
352 writeln!(
353 w,
354 "\t{visibility} struct {type_name}(Object<{sys_crate_name}::{glib_name}{class_name}>){parents_string};",
355 )?;
356 }
357 writeln!(w)?;
358 writeln!(w, "\tmatch fn {{")?;
359 writeln!(w, "\t\ttype_ => || {sys_crate_name}::{glib_func_name}(),")?;
360 writeln!(w, "\t}}")?;
361 writeln!(w, "}}")?;
362
363 Ok(())
364}
365
366fn define_boxed_type_internal(
367 w: &mut dyn Write,
368 env: &Env,
369 type_name: &str,
370 glib_name: &str,
371 copy_fn: &TraitInfo,
372 free_fn: &str,
373 boxed_inline: bool,
374 init_function_expression: &Option<String>,
375 copy_into_function_expression: &Option<String>,
376 clear_function_expression: &Option<String>,
377 get_type_fn: Option<&str>,
378 derive: &[Derive],
379 visibility: Visibility,
380 type_id: TypeId,
381) -> Result<()> {
382 let sys_crate_name = env.sys_crate_import(type_id);
383 writeln!(w, "{} {{", use_glib_type(env, "wrapper!"))?;
384
385 derives(w, derive, 1)?;
386 writeln!(
387 w,
388 "\t{} struct {}(Boxed{}<{}::{}>);",
389 visibility,
390 type_name,
391 if boxed_inline { "Inline" } else { "" },
392 sys_crate_name,
393 glib_name
394 )?;
395 writeln!(w)?;
396 writeln!(w, "\tmatch fn {{")?;
397 let mut_ov = if copy_fn.first_parameter_mut {
398 "mut_override(ptr)"
399 } else {
400 "ptr"
401 };
402 writeln!(
403 w,
404 "\t\tcopy => |ptr| {}::{}({}),",
405 sys_crate_name, copy_fn.glib_name, mut_ov
406 )?;
407 writeln!(w, "\t\tfree => |ptr| {sys_crate_name}::{free_fn}(ptr),")?;
408
409 if let (
410 Some(init_function_expression),
411 Some(copy_into_function_expression),
412 Some(clear_function_expression),
413 ) = (
414 init_function_expression,
415 copy_into_function_expression,
416 clear_function_expression,
417 ) {
418 writeln!(w, "\t\tinit => {init_function_expression},",)?;
419 writeln!(w, "\t\tcopy_into => {copy_into_function_expression},",)?;
420 writeln!(w, "\t\tclear => {clear_function_expression},",)?;
421 }
422
423 if let Some(get_type_fn) = get_type_fn {
424 writeln!(w, "\t\ttype_ => || {sys_crate_name}::{get_type_fn}(),")?;
425 }
426 writeln!(w, "\t}}")?;
427 writeln!(w, "}}")?;
428
429 Ok(())
430}
431
432pub fn define_boxed_type(
433 w: &mut dyn Write,
434 env: &Env,
435 type_name: &str,
436 glib_name: &str,
437 copy_fn: &TraitInfo,
438 free_fn: &str,
439 boxed_inline: bool,
440 init_function_expression: &Option<String>,
441 copy_into_function_expression: &Option<String>,
442 clear_function_expression: &Option<String>,
443 get_type_fn: Option<(String, Option<Version>)>,
444 derive: &[Derive],
445 visibility: Visibility,
446 type_id: TypeId,
447) -> Result<()> {
448 writeln!(w)?;
449
450 if let Some((ref get_type_fn, get_type_version)) = get_type_fn {
451 if get_type_version.is_some() {
452 version_condition(w, env, None, get_type_version, false, 0)?;
453 define_boxed_type_internal(
454 w,
455 env,
456 type_name,
457 glib_name,
458 copy_fn,
459 free_fn,
460 boxed_inline,
461 init_function_expression,
462 copy_into_function_expression,
463 clear_function_expression,
464 Some(get_type_fn),
465 derive,
466 visibility,
467 type_id,
468 )?;
469
470 writeln!(w)?;
471 not_version_condition_no_docsrs(w, env, None, get_type_version, false, 0)?;
472 define_boxed_type_internal(
473 w,
474 env,
475 type_name,
476 glib_name,
477 copy_fn,
478 free_fn,
479 boxed_inline,
480 init_function_expression,
481 copy_into_function_expression,
482 clear_function_expression,
483 None,
484 derive,
485 visibility,
486 type_id,
487 )?;
488 } else {
489 define_boxed_type_internal(
490 w,
491 env,
492 type_name,
493 glib_name,
494 copy_fn,
495 free_fn,
496 boxed_inline,
497 init_function_expression,
498 copy_into_function_expression,
499 clear_function_expression,
500 Some(get_type_fn),
501 derive,
502 visibility,
503 type_id,
504 )?;
505 }
506 } else {
507 define_boxed_type_internal(
508 w,
509 env,
510 type_name,
511 glib_name,
512 copy_fn,
513 free_fn,
514 boxed_inline,
515 init_function_expression,
516 copy_into_function_expression,
517 clear_function_expression,
518 None,
519 derive,
520 visibility,
521 type_id,
522 )?;
523 }
524
525 Ok(())
526}
527
528pub fn define_auto_boxed_type(
529 w: &mut dyn Write,
530 env: &Env,
531 type_name: &str,
532 glib_name: &str,
533 boxed_inline: bool,
534 init_function_expression: &Option<String>,
535 copy_into_function_expression: &Option<String>,
536 clear_function_expression: &Option<String>,
537 get_type_fn: &str,
538 derive: &[Derive],
539 visibility: Visibility,
540 type_id: TypeId,
541) -> Result<()> {
542 let sys_crate_name = env.sys_crate_import(type_id);
543 writeln!(w)?;
544 writeln!(w, "{} {{", use_glib_type(env, "wrapper!"))?;
545 derives(w, derive, 1)?;
546 writeln!(
547 w,
548 "\t{} struct {}(Boxed{}<{}::{}>);",
549 visibility,
550 type_name,
551 if boxed_inline { "Inline" } else { "" },
552 sys_crate_name,
553 glib_name
554 )?;
555 writeln!(w)?;
556 writeln!(w, "\tmatch fn {{")?;
557 writeln!(
558 w,
559 "\t\tcopy => |ptr| {}({}::{}(), ptr as *mut _) as *mut {}::{},",
560 use_glib_type(env, "gobject_ffi::g_boxed_copy"),
561 sys_crate_name,
562 get_type_fn,
563 sys_crate_name,
564 glib_name
565 )?;
566 writeln!(
567 w,
568 "\t\tfree => |ptr| {}({}::{}(), ptr as *mut _),",
569 use_glib_type(env, "gobject_ffi::g_boxed_free"),
570 sys_crate_name,
571 get_type_fn
572 )?;
573
574 if let (
575 Some(init_function_expression),
576 Some(copy_into_function_expression),
577 Some(clear_function_expression),
578 ) = (
579 init_function_expression,
580 copy_into_function_expression,
581 clear_function_expression,
582 ) {
583 writeln!(w, "\t\tinit => {init_function_expression},",)?;
584 writeln!(w, "\t\tcopy_into => {copy_into_function_expression},",)?;
585 writeln!(w, "\t\tclear => {clear_function_expression},",)?;
586 }
587
588 writeln!(w, "\t\ttype_ => || {sys_crate_name}::{get_type_fn}(),")?;
589 writeln!(w, "\t}}")?;
590 writeln!(w, "}}")?;
591
592 Ok(())
593}
594
595fn define_shared_type_internal(
596 w: &mut dyn Write,
597 env: &Env,
598 type_name: &str,
599 glib_name: &str,
600 ref_fn: &str,
601 unref_fn: &str,
602 get_type_fn: Option<&str>,
603 derive: &[Derive],
604 visibility: Visibility,
605 type_id: TypeId,
606) -> Result<()> {
607 let sys_crate_name = env.sys_crate_import(type_id);
608 writeln!(w, "{} {{", use_glib_type(env, "wrapper!"))?;
609 derives(w, derive, 1)?;
610 writeln!(
611 w,
612 "\t{visibility} struct {type_name}(Shared<{sys_crate_name}::{glib_name}>);"
613 )?;
614 writeln!(w)?;
615 writeln!(w, "\tmatch fn {{")?;
616 writeln!(w, "\t\tref => |ptr| {sys_crate_name}::{ref_fn}(ptr),")?;
617 writeln!(w, "\t\tunref => |ptr| {sys_crate_name}::{unref_fn}(ptr),")?;
618 if let Some(get_type_fn) = get_type_fn {
619 writeln!(w, "\t\ttype_ => || {sys_crate_name}::{get_type_fn}(),")?;
620 }
621 writeln!(w, "\t}}")?;
622 writeln!(w, "}}")?;
623
624 Ok(())
625}
626
627pub fn define_shared_type(
628 w: &mut dyn Write,
629 env: &Env,
630 type_name: &str,
631 glib_name: &str,
632 ref_fn: &str,
633 unref_fn: &str,
634 get_type_fn: Option<(String, Option<Version>)>,
635 derive: &[Derive],
636 visibility: Visibility,
637 type_id: TypeId,
638) -> Result<()> {
639 writeln!(w)?;
640
641 if let Some((ref get_type_fn, get_type_version)) = get_type_fn {
642 if get_type_version.is_some() {
643 version_condition(w, env, None, get_type_version, false, 0)?;
644 define_shared_type_internal(
645 w,
646 env,
647 type_name,
648 glib_name,
649 ref_fn,
650 unref_fn,
651 Some(get_type_fn),
652 derive,
653 visibility,
654 type_id,
655 )?;
656
657 writeln!(w)?;
658 not_version_condition_no_docsrs(w, env, None, get_type_version, false, 0)?;
659 define_shared_type_internal(
660 w, env, type_name, glib_name, ref_fn, unref_fn, None, derive, visibility, type_id,
661 )?;
662 } else {
663 define_shared_type_internal(
664 w,
665 env,
666 type_name,
667 glib_name,
668 ref_fn,
669 unref_fn,
670 Some(get_type_fn),
671 derive,
672 visibility,
673 type_id,
674 )?;
675 }
676 } else {
677 define_shared_type_internal(
678 w, env, type_name, glib_name, ref_fn, unref_fn, None, derive, visibility, type_id,
679 )?;
680 }
681
682 Ok(())
683}
684
685pub fn cfg_deprecated(
686 w: &mut dyn Write,
687 env: &Env,
688 type_tid: Option<TypeId>,
689 deprecated: Option<Version>,
690 commented: bool,
691 indent: usize,
692) -> Result<()> {
693 if let Some(s) = cfg_deprecated_string(env, type_tid, deprecated, commented, indent) {
694 writeln!(w, "{s}")?;
695 }
696 Ok(())
697}
698
699pub fn cfg_deprecated_string(
700 env: &Env,
701 type_tid: Option<TypeId>,
702 deprecated: Option<Version>,
703 commented: bool,
704 indent: usize,
705) -> Option<String> {
706 let comment = if commented { "//" } else { "" };
707 deprecated.map(|v| {
708 if env.is_too_low_version(type_tid.map(|t| t.ns_id), Some(v)) {
709 format!("{}{}#[deprecated = \"Since {}\"]", tabs(indent), comment, v)
710 } else {
711 format!(
712 "{}{}#[cfg_attr({}, deprecated = \"Since {}\")]",
713 tabs(indent),
714 comment,
715 v.to_cfg(None),
716 v,
717 )
718 }
719 })
720}
721
722pub fn version_condition(
723 w: &mut dyn Write,
724 env: &Env,
725 ns_id: Option<u16>,
726 version: Option<Version>,
727 commented: bool,
728 indent: usize,
729) -> Result<()> {
730 if let Some(s) = version_condition_string(env, ns_id, version, commented, indent) {
731 writeln!(w, "{s}")?;
732 }
733 Ok(())
734}
735
736pub fn version_condition_no_doc(
737 w: &mut dyn Write,
738 env: &Env,
739 ns_id: Option<u16>,
740 version: Option<Version>,
741 commented: bool,
742 indent: usize,
743) -> Result<()> {
744 let to_compare_with = env.config.min_required_version(env, ns_id);
745 let should_generate = match (version, to_compare_with) {
746 (Some(v), Some(to_compare_v)) => v > to_compare_v,
747 (Some(_), _) => true,
748 _ => false,
749 };
750 if should_generate {
751 let namespace_name = ns_id.and_then(|ns| {
753 if ns == namespaces::MAIN {
754 None
755 } else {
756 Some(env.namespaces.index(ns).crate_name.clone())
757 }
758 });
759 if let Some(s) = cfg_condition_string_no_doc(
760 Some(&version.unwrap().to_cfg(namespace_name.as_deref())),
761 commented,
762 indent,
763 ) {
764 writeln!(w, "{s}")?;
765 }
766 }
767 Ok(())
768}
769pub fn version_condition_doc(
770 w: &mut dyn Write,
771 env: &Env,
772 version: Option<Version>,
773 commented: bool,
774 indent: usize,
775) -> Result<()> {
776 match version {
777 Some(v) if v > env.config.min_cfg_version => {
778 if let Some(s) = cfg_condition_string_doc(Some(&v.to_cfg(None)), commented, indent) {
779 writeln!(w, "{s}")?;
780 }
781 }
782 _ => {}
783 }
784 Ok(())
785}
786
787pub fn version_condition_string(
788 env: &Env,
789 ns_id: Option<u16>,
790 version: Option<Version>,
791 commented: bool,
792 indent: usize,
793) -> Option<String> {
794 let to_compare_with = env.config.min_required_version(env, ns_id);
795 let should_generate = match (version, to_compare_with) {
796 (Some(v), Some(to_compare_v)) => v > to_compare_v,
797 (Some(_), _) => true,
798 _ => false,
799 };
800 if should_generate {
801 let namespace_name = ns_id.and_then(|ns| {
803 if ns == namespaces::MAIN {
804 None
805 } else {
806 Some(env.namespaces.index(ns).crate_name.clone())
807 }
808 });
809 cfg_condition_string(
810 Some(&version.unwrap().to_cfg(namespace_name.as_deref())),
811 commented,
812 indent,
813 )
814 } else {
815 None
816 }
817}
818
819pub fn not_version_condition(
820 w: &mut dyn Write,
821 version: Option<Version>,
822 commented: bool,
823 indent: usize,
824) -> Result<()> {
825 if let Some(s) = version.and_then(|v| {
826 cfg_condition_string(Some(&format!("not({})", v.to_cfg(None))), commented, indent)
827 }) {
828 writeln!(w, "{s}")?;
829 }
830 Ok(())
831}
832
833pub fn not_version_condition_no_docsrs(
834 w: &mut dyn Write,
835 env: &Env,
836 ns_id: Option<u16>,
837 version: Option<Version>,
838 commented: bool,
839 indent: usize,
840) -> Result<()> {
841 if let Some(v) = version {
842 let comment = if commented { "//" } else { "" };
843 let namespace_name = ns_id.and_then(|ns| {
844 if ns == namespaces::MAIN {
845 None
846 } else {
847 Some(env.namespaces.index(ns).crate_name.clone())
848 }
849 });
850 let s = format!(
851 "{}{}#[cfg(not(any({})))]",
852 tabs(indent),
853 comment,
854 v.to_cfg(namespace_name.as_deref())
855 );
856 writeln!(w, "{s}")?;
857 }
858 Ok(())
859}
860
861pub fn cfg_condition(
862 w: &mut dyn Write,
863 cfg_condition: Option<&(impl Display + ?Sized)>,
864 commented: bool,
865 indent: usize,
866) -> Result<()> {
867 if let Some(s) = cfg_condition_string(cfg_condition, commented, indent) {
868 writeln!(w, "{s}")?;
869 }
870 Ok(())
871}
872
873pub fn cfg_condition_no_doc(
874 w: &mut dyn Write,
875 cfg_condition: Option<&(impl Display + ?Sized)>,
876 commented: bool,
877 indent: usize,
878) -> Result<()> {
879 if let Some(s) = cfg_condition_string_no_doc(cfg_condition, commented, indent) {
880 writeln!(w, "{s}")?;
881 }
882 Ok(())
883}
884
885pub fn cfg_condition_string_no_doc(
886 cfg_condition: Option<&(impl Display + ?Sized)>,
887 commented: bool,
888 indent: usize,
889) -> Option<String> {
890 cfg_condition.map(|cfg| {
891 let comment = if commented { "//" } else { "" };
892 format!("{0}{1}#[cfg({2})]", tabs(indent), comment, cfg)
893 })
894}
895
896pub fn cfg_condition_doc(
897 w: &mut dyn Write,
898 cfg_condition: Option<&(impl Display + ?Sized)>,
899 commented: bool,
900 indent: usize,
901) -> Result<()> {
902 if let Some(s) = cfg_condition_string_doc(cfg_condition, commented, indent) {
903 writeln!(w, "{s}")?;
904 }
905 Ok(())
906}
907
908pub fn cfg_condition_string_doc(
909 cfg_condition: Option<&(impl Display + ?Sized)>,
910 commented: bool,
911 indent: usize,
912) -> Option<String> {
913 cfg_condition.map(|cfg| {
914 let comment = if commented { "//" } else { "" };
915 format!(
916 "{0}{1}#[cfg_attr(docsrs, doc(cfg({2})))]",
917 tabs(indent),
918 comment,
919 cfg,
920 )
921 })
922}
923
924pub fn cfg_condition_string(
925 cfg_condition: Option<&(impl Display + ?Sized)>,
926 commented: bool,
927 indent: usize,
928) -> Option<String> {
929 cfg_condition.map(|_| {
930 format!(
931 "{}\n{}",
932 cfg_condition_string_no_doc(cfg_condition, commented, indent).unwrap(),
933 cfg_condition_string_doc(cfg_condition, commented, indent).unwrap(),
934 )
935 })
936}
937
938pub fn derives(w: &mut dyn Write, derives: &[Derive], indent: usize) -> Result<()> {
939 for derive in derives {
940 let s = match &derive.cfg_condition {
941 Some(condition) => format!(
942 "#[cfg_attr({}, derive({}))]",
943 condition,
944 derive.names.join(", ")
945 ),
946 None => format!("#[derive({})]", derive.names.join(", ")),
947 };
948 writeln!(w, "{}{}", tabs(indent), s)?;
949 }
950 Ok(())
951}
952
953pub fn doc_alias(w: &mut dyn Write, name: &str, comment_prefix: &str, indent: usize) -> Result<()> {
954 writeln!(
955 w,
956 "{}{}#[doc(alias = \"{}\")]",
957 tabs(indent),
958 comment_prefix,
959 name,
960 )
961}
962
963pub fn external_doc_link(
964 w: &mut dyn Write,
965 external_url: Option<&str>,
966 name: &str,
967 visibility: &Visibility,
968 indent: usize,
969) -> Result<()> {
970 if !visibility.is_public() {
972 Ok(())
973 } else if let Some(external_url) = external_url {
974 writeln!(
975 w,
976 "{}/// This documentation is incomplete due to license restrictions and limitations on docs.rs. Please have a look at [our official docs]({}/index.html?search={}) for more information.",
977 tabs(indent),
978 external_url.trim_end_matches('/'),
979 name
980 )
981 } else {
982 Ok(())
983 }
984}
985
986pub fn doc_hidden(
987 w: &mut dyn Write,
988 doc_hidden: bool,
989 comment_prefix: &str,
990 indent: usize,
991) -> Result<()> {
992 if doc_hidden {
993 writeln!(w, "{}{}#[doc(hidden)]", tabs(indent), comment_prefix)
994 } else {
995 Ok(())
996 }
997}
998
999pub fn allow_deprecated(
1000 w: &mut dyn Write,
1001 allow_deprecated: Option<Version>,
1002 commented: bool,
1003 indent: usize,
1004) -> Result<()> {
1005 if allow_deprecated.is_some() {
1006 writeln!(
1007 w,
1008 "{}{}#[allow(deprecated)]",
1009 tabs(indent),
1010 if commented { "//" } else { "" }
1011 )
1012 } else {
1013 Ok(())
1014 }
1015}
1016
1017pub fn write_vec<T: Display>(w: &mut dyn Write, v: &[T]) -> Result<()> {
1018 for s in v {
1019 writeln!(w, "{s}")?;
1020 }
1021 Ok(())
1022}
1023
1024pub fn declare_default_from_new(
1025 w: &mut dyn Write,
1026 env: &Env,
1027 name: &str,
1028 functions: &[analysis::functions::Info],
1029 has_builder: bool,
1030) -> Result<()> {
1031 if let Some(func) = functions.iter().find(|f| {
1032 !f.hidden
1033 && f.status.need_generate()
1034 && f.name == "new"
1035 && f.ret.parameter.as_ref().is_some_and(|x| !*x.lib_par.nullable)
1037 }) {
1038 if func.parameters.rust_parameters.is_empty() {
1039 writeln!(w)?;
1040 version_condition(w, env, None, func.version, false, 0)?;
1041 writeln!(
1042 w,
1043 "impl Default for {name} {{
1044 fn default() -> Self {{
1045 Self::new()
1046 }}
1047 }}"
1048 )?;
1049 } else if has_builder {
1050 writeln!(w)?;
1052 version_condition(w, env, None, func.version, false, 0)?;
1053 writeln!(
1054 w,
1055 "impl Default for {name} {{
1056 fn default() -> Self {{
1057 glib::object::Object::new::<Self>()
1058 }}
1059 }}"
1060 )?;
1061 }
1062 }
1063
1064 Ok(())
1065}
1066
1067pub fn escape_string(s: &str) -> String {
1069 let mut es = String::with_capacity(s.len() * 2);
1070 for c in s.chars() {
1071 match c {
1072 '\"' | '\\' => es.push('\\'),
1073 _ => (),
1074 }
1075 es.push(c);
1076 }
1077 es
1078}
1079
1080#[cfg(test)]
1081mod tests {
1082 use super::*;
1083
1084 #[test]
1085 fn test_escape_string() {
1086 assert_eq!(escape_string(""), "");
1087 assert_eq!(escape_string("no escaping here"), "no escaping here");
1088 assert_eq!(escape_string(r#"'"\"#), r#"'\"\\"#);
1089 }
1090}