summaryrefslogtreecommitdiff
path: root/src/bin
diff options
context:
space:
mode:
authorcos <cos>2024-06-07 08:35:58 +0200
committercos <cos>2024-06-07 08:36:04 +0200
commit55772f9b596e5cafd328695f0dc7f5741543c187 (patch)
tree7c1f7ad239e32bf618433e6385df43a50b74b70b /src/bin
parent89052221c847f8f4e24ae1f5bd37759a60d0e173 (diff)
downloadccc-55772f9b596e5cafd328695f0dc7f5741543c187.zip
explore: Unsuccessful attempt at using kaldav cratetopic/caldav-crates
Diffstat (limited to 'src/bin')
-rw-r--r--src/bin/caldav-crates.rs87
-rw-r--r--src/bin/ccc.rs170
2 files changed, 257 insertions, 0 deletions
diff --git a/src/bin/caldav-crates.rs b/src/bin/caldav-crates.rs
new file mode 100644
index 0000000..258b248
--- /dev/null
+++ b/src/bin/caldav-crates.rs
@@ -0,0 +1,87 @@
+use {
+ anyhow::{
+ Result,
+
+ anyhow,
+ },
+ std::env::args,
+};
+
+struct CrateKaldav {
+ client: kaldav::Client,
+}
+
+impl CrateKaldav {
+ fn new(url: &str, username: String, password: Option<String>) -> Self {
+ println!("kaldav");
+
+ let mut client = kaldav::Client::new(url);
+ client.set_auth(Some(kaldav::Authorization { username, password, }));
+
+ Self {
+ client,
+ }
+ }
+
+ fn show_content(&self) -> Result<()> {
+ // This function does not do what we want, because Principal::home() does not appear to
+ // work in our environment.
+ // Reading https://datatracker.ietf.org/doc/html/rfc4791#section-6.2.1 reveals that the
+ // CALDAV:calendar-home-set property merely SHOULD, rather than MUST, be implemented.
+
+ let principals = self.client.principals()?;
+ // eprintln!("Principals: {principals:?}");
+
+ let mut home = None;
+ println!("Attempting to find home.");
+ for p in principals {
+ home = p.home().ok();
+ if home.is_some() {
+ break;
+ }
+ }
+ if home.is_some() {
+ println!("Found home: {home:?}");
+ } else {
+ return Err(anyhow!("No home found in any Principal. :("));
+ }
+
+ // This function is heavily based on:
+ // https://github.com/sanpii/kaldav/blob/main/examples/list_events.rs
+ eprintln!("Retrieving list of calendars.");
+ let calendars = self.client.calendars()?;
+
+ for (name, calendar) in calendars {
+ println!("Calendar '{}'", name);
+
+ let objects = calendar.events()?;
+
+ if objects.is_empty() {
+ println!(" no events");
+ continue;
+ }
+
+ for object in objects {
+ for event in object.events {
+ println!(
+ " {} - {}",
+ event.dtstart,
+ event.summary.unwrap_or_default()
+ );
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+fn main() -> Result<()> {
+ let caldav_url = args().nth(1).expect("Missing caldav url argument.");
+ let username = args().nth(2).expect("Missing username argument.");
+ let password = args().nth(3).expect("Missing password argument.");
+
+ let kaldav = CrateKaldav::new(&caldav_url, username.clone(), Some(password.clone()));
+ kaldav.show_content()?;
+
+ Ok(())
+}
diff --git a/src/bin/ccc.rs b/src/bin/ccc.rs
new file mode 100644
index 0000000..13d24d6
--- /dev/null
+++ b/src/bin/ccc.rs
@@ -0,0 +1,170 @@
+use {
+ crossbeam_channel::{
+ Receiver,
+ Sender,
+
+ unbounded,
+ },
+ cursive::{
+ Cursive,
+ CursiveExt,
+ Printer,
+ Rect,
+ View,
+ XY,
+
+ direction::Direction,
+ event::{
+ Event,
+ EventResult,
+ },
+ view::{
+ CannotFocus,
+ Nameable,
+ Selector,
+ ViewNotFound,
+ },
+ views::{
+ Button,
+ LinearLayout,
+ TextView,
+ }
+ },
+ std::{
+ time::Duration,
+ thread::{
+ self,
+ sleep,
+ },
+ }
+};
+
+type CursiveSender = Sender<Box<(dyn for<'a> FnOnce(&'a mut Cursive) + Send + 'static)>>;
+
+struct DataView {
+ data: Option<String>,
+ view: TextView,
+}
+
+impl DataView {
+ pub fn new() -> Self {
+ Self {
+ data: None,
+ view: TextView::new("uninitialized"),
+ }
+ }
+
+ fn set_data(&mut self, data: &str) {
+ self.data = Some(data.to_owned());
+ self.view.set_content(data.to_string())
+ }
+}
+
+impl View for DataView {
+ fn draw(&self, printer: &Printer<'_, '_>) {
+ self.view.draw(printer)
+ }
+
+ fn layout(&mut self, xy: XY<usize>) {
+ self.view.layout(xy)
+ }
+
+ fn needs_relayout(&self) -> bool {
+ self.view.needs_relayout()
+ }
+
+ fn required_size(&mut self, constraint: XY<usize>) -> XY<usize> {
+ self.view.required_size(constraint)
+ }
+
+ fn on_event(&mut self, event: Event) -> EventResult {
+ self.view.on_event(event)
+ }
+
+ // fn call_on_any(), should not be needed for this view.
+
+ fn focus_view(&mut self, selector: &Selector<'_>) -> Result<EventResult, ViewNotFound> {
+ self.view.focus_view(selector)
+ }
+
+ fn take_focus( &mut self, source: Direction) -> Result<EventResult, CannotFocus> {
+ self.view.take_focus(source)
+ }
+
+ fn important_area(&self, view_size: XY<usize>) -> Rect {
+ self.view.important_area(view_size)
+ }
+
+ fn type_name(&self) -> &'static str {
+ "DataView"
+ }
+}
+
+fn data_request(cur: &mut Cursive) {
+ if let Some(sender) = cur.user_data::<Sender<bool>>() {
+ let _ = sender.send(true);
+ }
+}
+
+fn populate_data(s: &mut Cursive, is_data: bool) {
+ if let Some(mut data_view) = s.find_name::<DataView>("populate_me") {
+ if is_data {
+ data_view.set_data("populated")
+ } else {
+ data_view.set_data("initialized")
+ }
+ }
+}
+
+fn background_thread(reciever: Receiver<bool>, sender: CursiveSender) {
+ while let Ok(msg) = reciever.recv() {
+ eprintln!("Received a message. Sleeping 3 seconds before sending response.");
+ sleep(Duration::from_secs(3));
+ let _ = sender.send(Box::new(move |s| populate_data(s, msg)));
+ }
+}
+
+fn main() {
+ // In order to maintain the main thread responsive in dealing with the user interface, we wish
+ // to offload all server communication to a background thread. This code demonstrates how to
+ // send a message from the ui, process it in the background and send the result back for the
+ // user interface to update the content of its view.
+
+ // Firstly, we need a channel pair for sending a message from the ui to the worker thread.
+ let (wrk_sender, wrk_receiver) = unbounded::<bool>();
+
+ // We then construct the actual user interface. Our DataView, a button to trigger populating
+ // it and a button to quit the app.
+ let mut app = Cursive::new();
+ let mut horizontal = LinearLayout::horizontal();
+ let mut vertical = LinearLayout::vertical();
+ let data_view = DataView::new().with_name("populate_me");
+ let populate_button = Button::new("data", data_request);
+ let quit_button = Button::new("quit", |s| s.quit());
+ vertical.add_child(data_view);
+ horizontal.add_child(populate_button);
+ horizontal.add_child(quit_button);
+ vertical.add_child(horizontal);
+ app.add_layer(vertical);
+
+ // Once having created the app, we send a request to initialize our view.
+ let _ = wrk_sender.send(false);
+
+ // We then place the sender in the user_data, to make it available for any ui function.
+ app.set_user_data(wrk_sender);
+
+ // Getting the callback sender from the user interface, needed for the worker to respond.
+ let ui_sender = app.cb_sink().clone();
+
+ // Then we launch the background worker thread, providing it the appropriate channel ends.
+ let _worker = thread::spawn(move || background_thread(wrk_receiver, ui_sender));
+
+ // Lastly we launch the user interface.
+ app.run();
+
+ // Three seconds after launching, the text changes from "uninitialized" to "initialized" and
+ // further, three seconds after pressing the <data> button the text becomes "populated".
+ //
+ // Essentially, this design should work for the complete program. Yet with some slightly more
+ // expressive choice of datatypes than merely a bool.
+}