1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#[cfg(feature = "v0_4_11")]
use crate::pw::LinkState;
use crate::{
	core::Core,
	prelude::*,
	pw::{self, Direction, Link, Node, Port, Properties},
};

impl Link {
	#[doc(alias("wp_link_new_from_factory"))]
	pub fn new<O: LinkTarget + Debug, I: LinkTarget + Debug>(
		core: &Core,
		output: &O,
		input: &I,
		props: &Properties,
	) -> Result<Self, Error> {
		let props = Properties::new_clone(props);
		output.write_props(&props, Direction::Output)?;
		input.write_props(&props, Direction::Input)?;

		Self::from_factory(core, "link-factory", Some(props))
			.ok_or_else(|| Error::new(LibraryErrorEnum::OperationFailed, "factory did not produce a link???"))
	}

	pub fn error_is_exists(e: &Error) -> bool {
		e.message().ends_with(": File exists") // TODO
	}

	#[cfg(feature = "v0_4_11")]
	#[cfg_attr(docsrs, doc(cfg(feature = "v0_4_11")))]
	#[doc(alias = "wp_link_get_state")]
	#[doc(alias = "get_state")]
	pub fn state(&self) -> LinkState {
		unsafe { from_glib(ffi::wp_link_get_state(self.to_glib_none().0, ptr::null_mut())) }
	}

	#[cfg(feature = "v0_4_11")]
	#[cfg_attr(docsrs, doc(cfg(feature = "v0_4_11")))]
	#[doc(alias = "wp_link_get_state")]
	#[doc(alias = "get_state")]
	pub fn state_result(&self) -> Result<LinkState, Error> {
		unsafe {
			let mut error = ptr::null();
			match from_glib(ffi::wp_link_get_state(self.to_glib_none().0, &mut error)) {
				LinkState::Error => {
					let msg: Option<&glib::GStr> = from_glib_none(error);
					Err(Error::new(
						LibraryErrorEnum::OperationFailed,
						msg.map(|s| s.as_str()).unwrap_or("unspecified link state error"),
					))
				},
				state => Ok(state),
			}
		}
	}
}

pub trait LinkTarget {
	fn write_props(&self, props: &Properties, dir: Direction) -> Result<(), Error>;
}

impl LinkTarget for Node {
	fn write_props(&self, props: &Properties, dir: Direction) -> Result<(), Error> {
		match dir {
			Direction::Output => props.insert(pw::PW_KEY_LINK_OUTPUT_NODE, self.bound_id()),
			Direction::Input => props.insert(pw::PW_KEY_LINK_INPUT_NODE, self.bound_id()),
			_ => unreachable!(),
		}
		Ok(())
	}
}

impl LinkTarget for Port {
	fn write_props(&self, props: &Properties, dir: Direction) -> Result<(), Error> {
		let node_id = self.node_id()?;
		match dir {
			Direction::Output => {
				props.insert(pw::PW_KEY_LINK_OUTPUT_PORT, self.port_index()?);
				props.insert(pw::PW_KEY_LINK_OUTPUT_NODE, node_id);
			},
			Direction::Input => {
				props.insert(pw::PW_KEY_LINK_INPUT_PORT, self.port_index()?);
				props.insert(pw::PW_KEY_LINK_INPUT_NODE, node_id);
			},
			_ => unreachable!(),
		}
		Ok(())
	}
}

#[cfg(not(feature = "v0_4_11"))]
impl StaticType for pw::LinkFeatures {
	fn static_type() -> Type {
		pw::ProxyFeatures::static_type()
	}
}

#[cfg(feature = "v0_4_11")]
impl<E> From<Result<LinkState, E>> for LinkState {
	fn from(res: Result<LinkState, E>) -> Self {
		match res {
			Ok(state) => state,
			Err(_) => LinkState::Error,
		}
	}
}

impl Direction {
	pub fn name(&self) -> &'static str {
		match self {
			Direction::Input => "input",
			Direction::Output => "output",
			Direction::__Unknown(direction) => panic!("unknown WP_DIRECTION {direction}"),
		}
	}
}

impl fmt::Display for Direction {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		f.write_str(self.name())
	}
}

impl FromStr for Direction {
	type Err = Error;

	fn from_str(s: &str) -> Result<Self, Self::Err> {
		Ok(match s {
			"input" => Direction::Input,
			"output" => Direction::Output,
			_ =>
				return Err(Error::new(
					LibraryErrorEnum::InvalidArgument,
					&format!("unknown direction {s}"),
				)),
		})
	}
}