summaryrefslogtreecommitdiffstats
path: root/toolkit/components/uniffi-fixtures/todolist
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:35:37 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:35:37 +0000
commita90a5cba08fdf6c0ceb95101c275108a152a3aed (patch)
tree532507288f3defd7f4dcf1af49698bcb76034855 /toolkit/components/uniffi-fixtures/todolist
parentAdding debian version 126.0.1-1. (diff)
downloadfirefox-a90a5cba08fdf6c0ceb95101c275108a152a3aed.tar.xz
firefox-a90a5cba08fdf6c0ceb95101c275108a152a3aed.zip
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/uniffi-fixtures/todolist')
-rw-r--r--toolkit/components/uniffi-fixtures/todolist/Cargo.toml22
-rw-r--r--toolkit/components/uniffi-fixtures/todolist/build.rs7
-rw-r--r--toolkit/components/uniffi-fixtures/todolist/src/lib.rs150
-rw-r--r--toolkit/components/uniffi-fixtures/todolist/src/todolist.udl38
-rw-r--r--toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.kts83
-rw-r--r--toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.py56
-rw-r--r--toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.rb47
-rw-r--r--toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.swift69
-rw-r--r--toolkit/components/uniffi-fixtures/todolist/tests/test_generated_bindings.rs6
9 files changed, 478 insertions, 0 deletions
diff --git a/toolkit/components/uniffi-fixtures/todolist/Cargo.toml b/toolkit/components/uniffi-fixtures/todolist/Cargo.toml
new file mode 100644
index 0000000000..6673d01e07
--- /dev/null
+++ b/toolkit/components/uniffi-fixtures/todolist/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "uniffi-example-todolist"
+edition = "2021"
+version = "0.22.0"
+authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
+license = "MPL-2.0"
+publish = false
+
+[lib]
+crate-type = ["lib", "cdylib"]
+name = "uniffi_todolist"
+
+[dependencies]
+uniffi = { workspace = true }
+once_cell = "1.12"
+thiserror = "1.0"
+
+[build-dependencies]
+uniffi = { workspace = true, features = ["build"] }
+
+[dev-dependencies]
+uniffi = { workspace = true, features = ["bindgen-tests"] }
diff --git a/toolkit/components/uniffi-fixtures/todolist/build.rs b/toolkit/components/uniffi-fixtures/todolist/build.rs
new file mode 100644
index 0000000000..2dd2f68b75
--- /dev/null
+++ b/toolkit/components/uniffi-fixtures/todolist/build.rs
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+fn main() {
+ uniffi::generate_scaffolding("src/todolist.udl").unwrap();
+}
diff --git a/toolkit/components/uniffi-fixtures/todolist/src/lib.rs b/toolkit/components/uniffi-fixtures/todolist/src/lib.rs
new file mode 100644
index 0000000000..11cdc63aee
--- /dev/null
+++ b/toolkit/components/uniffi-fixtures/todolist/src/lib.rs
@@ -0,0 +1,150 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::sync::{Arc, RwLock};
+
+use once_cell::sync::Lazy;
+
+#[derive(Debug, Clone)]
+pub struct TodoEntry {
+ text: String,
+}
+
+// There is a single "default" TodoList that can be shared
+// by all consumers of this component. Depending on requirements,
+// a real app might like to use a `Weak<>` rather than an `Arc<>`
+// here to reduce the risk of circular references.
+static DEFAULT_LIST: Lazy<RwLock<Option<Arc<TodoList>>>> = Lazy::new(|| RwLock::new(None));
+
+#[derive(Debug, thiserror::Error)]
+pub enum TodoError {
+ #[error("The todo does not exist!")]
+ TodoDoesNotExist,
+ #[error("The todolist is empty!")]
+ EmptyTodoList,
+ #[error("That todo already exists!")]
+ DuplicateTodo,
+ #[error("Empty String error!: {0}")]
+ EmptyString(String),
+ #[error("I am a delegated Error: {0}")]
+ DeligatedError(#[from] std::io::Error),
+}
+
+/// Get a reference to the global default TodoList, if set.
+///
+fn get_default_list() -> Option<Arc<TodoList>> {
+ DEFAULT_LIST.read().unwrap().clone()
+}
+
+/// Set the global default TodoList.
+///
+/// This will silently drop any previously set value.
+///
+fn set_default_list(list: Arc<TodoList>) {
+ *DEFAULT_LIST.write().unwrap() = Some(list);
+}
+
+/// Create a new TodoEntry from the given string.
+///
+fn create_entry_with<S: Into<String>>(item: S) -> Result<TodoEntry> {
+ let text = item.into();
+ if text.is_empty() {
+ return Err(TodoError::EmptyString(
+ "Cannot add empty string as entry".to_string(),
+ ));
+ }
+ Ok(TodoEntry { text })
+}
+
+type Result<T, E = TodoError> = std::result::Result<T, E>;
+
+// A simple Todolist.
+// UniFFI requires that we use interior mutability for managing mutable state, so we wrap our `Vec` in a RwLock.
+// (A Mutex would also work, but a RwLock is more appropriate for this use-case, so we use it).
+#[derive(Debug)]
+pub struct TodoList {
+ items: RwLock<Vec<String>>,
+}
+
+impl TodoList {
+ fn new() -> Self {
+ Self {
+ items: RwLock::new(Vec::new()),
+ }
+ }
+
+ fn add_item<S: Into<String>>(&self, item: S) -> Result<()> {
+ let item = item.into();
+ if item.is_empty() {
+ return Err(TodoError::EmptyString(
+ "Cannot add empty string as item".to_string(),
+ ));
+ }
+ let mut items = self.items.write().unwrap();
+ if items.contains(&item) {
+ return Err(TodoError::DuplicateTodo);
+ }
+ items.push(item);
+ Ok(())
+ }
+
+ fn get_last(&self) -> Result<String> {
+ let items = self.items.read().unwrap();
+ items.last().cloned().ok_or(TodoError::EmptyTodoList)
+ }
+
+ fn get_first(&self) -> Result<String> {
+ let items = self.items.read().unwrap();
+ items.first().cloned().ok_or(TodoError::EmptyTodoList)
+ }
+
+ fn add_entries(&self, entries: Vec<TodoEntry>) {
+ let mut items = self.items.write().unwrap();
+ items.extend(entries.into_iter().map(|e| e.text))
+ }
+
+ fn add_entry(&self, entry: TodoEntry) -> Result<()> {
+ self.add_item(entry.text)
+ }
+
+ fn add_items<S: Into<String>>(&self, items: Vec<S>) {
+ let mut my_items = self.items.write().unwrap();
+ my_items.extend(items.into_iter().map(Into::into))
+ }
+
+ fn get_items(&self) -> Vec<String> {
+ let items = self.items.read().unwrap();
+ items.clone()
+ }
+
+ fn get_entries(&self) -> Vec<TodoEntry> {
+ let items = self.items.read().unwrap();
+ items
+ .iter()
+ .map(|text| TodoEntry { text: text.clone() })
+ .collect()
+ }
+
+ fn get_last_entry(&self) -> Result<TodoEntry> {
+ let text = self.get_last()?;
+ Ok(TodoEntry { text })
+ }
+
+ fn clear_item<S: Into<String>>(&self, item: S) -> Result<()> {
+ let item = item.into();
+ let mut items = self.items.write().unwrap();
+ let idx = items
+ .iter()
+ .position(|s| s == &item)
+ .ok_or(TodoError::TodoDoesNotExist)?;
+ items.remove(idx);
+ Ok(())
+ }
+
+ fn make_default(self: Arc<Self>) {
+ set_default_list(self);
+ }
+}
+
+uniffi::include_scaffolding!("todolist");
diff --git a/toolkit/components/uniffi-fixtures/todolist/src/todolist.udl b/toolkit/components/uniffi-fixtures/todolist/src/todolist.udl
new file mode 100644
index 0000000000..5c923314cd
--- /dev/null
+++ b/toolkit/components/uniffi-fixtures/todolist/src/todolist.udl
@@ -0,0 +1,38 @@
+namespace todolist {
+ TodoList? get_default_list();
+ undefined set_default_list(TodoList list);
+
+ [Throws=TodoError]
+ TodoEntry create_entry_with(string todo);
+};
+
+dictionary TodoEntry {
+ string text;
+};
+
+[Error]
+enum TodoError {
+ "TodoDoesNotExist", "EmptyTodoList", "DuplicateTodo", "EmptyString", "DeligatedError"
+};
+
+interface TodoList {
+ constructor();
+ [Throws=TodoError]
+ void add_item(string todo);
+ [Throws=TodoError]
+ void add_entry(TodoEntry entry);
+ sequence<TodoEntry> get_entries();
+ sequence<string> get_items();
+ void add_entries(sequence<TodoEntry> entries);
+ void add_items(sequence<string> items);
+ [Throws=TodoError]
+ TodoEntry get_last_entry();
+ [Throws=TodoError]
+ string get_last();
+ [Throws=TodoError]
+ string get_first();
+ [Throws=TodoError]
+ void clear_item(string todo);
+ [Self=ByArc]
+ undefined make_default();
+};
diff --git a/toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.kts b/toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.kts
new file mode 100644
index 0000000000..bb2b292224
--- /dev/null
+++ b/toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.kts
@@ -0,0 +1,83 @@
+import uniffi.todolist.*
+
+val todo = TodoList()
+
+// This throws an exception:
+try {
+ todo.getLast()
+ throw RuntimeException("Should have thrown a TodoError!")
+} catch (e: TodoException.EmptyTodoList) {
+ // It's okay, we don't have any items yet!
+}
+
+try {
+ createEntryWith("")
+ throw RuntimeException("Should have thrown a TodoError!")
+} catch (e: TodoException) {
+ // It's okay, the string was empty!
+ assert(e is TodoException.EmptyString)
+ assert(e !is TodoException.EmptyTodoList)
+}
+
+todo.addItem("Write strings support")
+
+assert(todo.getLast() == "Write strings support")
+
+todo.addItem("Write tests for strings support")
+
+assert(todo.getLast() == "Write tests for strings support")
+
+val entry = createEntryWith("Write bindings for strings as record members")
+
+todo.addEntry(entry)
+assert(todo.getLast() == "Write bindings for strings as record members")
+assert(todo.getLastEntry().text == "Write bindings for strings as record members")
+
+todo.addItem("Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣")
+assert(todo.getLast() == "Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣")
+
+val entry2 = TodoEntry("Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣")
+todo.addEntry(entry2)
+assert(todo.getLastEntry().text == "Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣")
+
+assert(todo.getEntries().size == 5)
+
+todo.addEntries(listOf(TodoEntry("foo"), TodoEntry("bar")))
+assert(todo.getEntries().size == 7)
+assert(todo.getLastEntry().text == "bar")
+
+todo.addItems(listOf("bobo", "fofo"))
+assert(todo.getItems().size == 9)
+assert(todo.getItems()[7] == "bobo")
+
+assert(getDefaultList() == null)
+
+// Note that each individual object instance needs to be explicitly destroyed,
+// either by using the `.use` helper or explicitly calling its `.destroy` method.
+// Failure to do so will leak the underlying Rust object.
+TodoList().use { todo2 ->
+ setDefaultList(todo)
+ getDefaultList()!!.use { default ->
+ assert(todo.getEntries() == default.getEntries())
+ assert(todo2.getEntries() != default.getEntries())
+ }
+
+ todo2.makeDefault()
+ getDefaultList()!!.use { default ->
+ assert(todo.getEntries() != default.getEntries())
+ assert(todo2.getEntries() == default.getEntries())
+ }
+
+ todo.addItem("Test liveness after being demoted from default")
+ assert(todo.getLast() == "Test liveness after being demoted from default")
+
+ todo2.addItem("Test shared state through local vs default reference")
+ getDefaultList()!!.use { default ->
+ assert(default.getLast() == "Test shared state through local vs default reference")
+ }
+}
+
+// Ensure the kotlin version of deinit doesn't crash, and is idempotent.
+todo.destroy()
+todo.destroy()
+
diff --git a/toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.py b/toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.py
new file mode 100644
index 0000000000..e4e2cda6d6
--- /dev/null
+++ b/toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.py
@@ -0,0 +1,56 @@
+from todolist import TodoEntry, TodoList, get_default_list, set_default_list
+
+todo = TodoList()
+
+entry = TodoEntry(text="Write bindings for strings in records")
+
+todo.add_item("Write python bindings")
+
+assert todo.get_last() == "Write python bindings"
+
+todo.add_item("Write tests for bindings")
+
+assert todo.get_last() == "Write tests for bindings"
+
+todo.add_entry(entry)
+
+assert todo.get_last() == "Write bindings for strings in records"
+assert todo.get_last_entry().text == "Write bindings for strings in records"
+
+todo.add_item(
+ "Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣"
+)
+assert (
+ todo.get_last()
+ == "Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣"
+)
+
+entry2 = TodoEntry(
+ text="Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣"
+)
+todo.add_entry(entry2)
+assert (
+ todo.get_last_entry().text
+ == "Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣"
+)
+
+todo2 = TodoList()
+assert todo != todo2
+assert todo is not todo2
+
+assert get_default_list() is None
+
+set_default_list(todo)
+assert todo.get_items() == get_default_list().get_items()
+
+todo2.make_default()
+assert todo2.get_items() == get_default_list().get_items()
+
+todo.add_item("Test liveness after being demoted from default")
+assert todo.get_last() == "Test liveness after being demoted from default"
+
+todo2.add_item("Test shared state through local vs default reference")
+assert (
+ get_default_list().get_last()
+ == "Test shared state through local vs default reference"
+)
diff --git a/toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.rb b/toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.rb
new file mode 100644
index 0000000000..fc1a823f52
--- /dev/null
+++ b/toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'test/unit'
+require 'todolist'
+
+include Test::Unit::Assertions
+include Todolist
+
+todo = TodoList.new
+entry = TodoEntry.new(text: 'Write bindings for strings in records')
+
+todo.add_item('Write ruby bindings')
+
+assert_equal todo.get_last, 'Write ruby bindings'
+
+todo.add_item('Write tests for bindings')
+
+assert_equal todo.get_last, 'Write tests for bindings'
+
+todo.add_entry(entry)
+
+assert_equal todo.get_last, 'Write bindings for strings in records'
+assert_equal todo.get_last_entry.text, 'Write bindings for strings in records'
+
+todo.add_item("Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣")
+assert_equal todo.get_last, "Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣"
+
+entry2 = TodoEntry.new(text: "Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣")
+todo.add_entry(entry2)
+assert_equal todo.get_last_entry.text, "Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣"
+
+todo2 = TodoList.new
+assert todo2.get_items != todo.get_items
+
+assert Todolist.get_default_list == nil
+
+Todolist.set_default_list todo
+assert todo.get_items == Todolist.get_default_list.get_items
+
+todo2.make_default
+assert todo2.get_items == Todolist.get_default_list.get_items
+
+todo.add_item "Test liveness after being demoted from default"
+assert todo.get_last == "Test liveness after being demoted from default"
+
+todo2.add_item "Test shared state through local vs default reference"
+assert Todolist.get_default_list.get_last == "Test shared state through local vs default reference"
diff --git a/toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.swift b/toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.swift
new file mode 100644
index 0000000000..6ce72cadb2
--- /dev/null
+++ b/toolkit/components/uniffi-fixtures/todolist/tests/bindings/test_todolist.swift
@@ -0,0 +1,69 @@
+import todolist
+
+
+let todo = TodoList()
+do {
+ let _ = try todo.getLast()
+ fatalError("Should have thrown an EmptyTodoList error!")
+} catch TodoError.EmptyTodoList{
+ //It's okay! There are not todos!
+}
+try! todo.addItem(todo: "Write swift bindings")
+assert( try! todo.getLast() == "Write swift bindings")
+
+try! todo.addItem(todo: "Write tests for bindings")
+assert(try! todo.getLast() == "Write tests for bindings")
+
+let entry = TodoEntry(text: "Write bindings for strings as record members")
+try! todo.addEntry(entry: entry)
+assert(try! todo.getLast() == "Write bindings for strings as record members")
+
+try! todo.addItem(todo: "Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣")
+assert(try! todo.getLast() == "Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣")
+
+do {
+ let _ = try createEntryWith(todo: "")
+ fatalError("Should have thrown an EmptyString error!")
+} catch TodoError.EmptyString {
+ // It's okay! It was an empty string
+}
+
+let entry2 = TodoEntry(text: "Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣")
+try! todo.addEntry(entry: entry2)
+assert(try! todo.getLastEntry() == entry2)
+
+assert(todo.getEntries().count == 5)
+
+todo.addEntries(entries: [TodoEntry(text: "foo"), TodoEntry(text: "bar")])
+assert(todo.getEntries().count == 7)
+assert(todo.getItems().count == 7)
+assert(try! todo.getLast() == "bar")
+
+todo.addItems(items: ["bobo", "fofo"])
+assert(todo.getItems().count == 9)
+assert(todo.getItems()[7] == "bobo")
+
+// Ensure deinit doesn't crash.
+for _ in 0..<10 {
+ let list = TodoList()
+ try! list.addItem(todo: "todo")
+}
+
+let todo2 = TodoList()
+
+assert(getDefaultList() == nil)
+
+setDefaultList(list: todo)
+assert(todo.getItems() == getDefaultList()!.getItems())
+assert(todo2.getItems() != getDefaultList()!.getItems())
+
+todo2.makeDefault()
+assert(todo.getItems() != getDefaultList()!.getItems())
+assert(todo2.getItems() == getDefaultList()!.getItems())
+
+try! todo.addItem(todo: "Test liveness after being demoted from default")
+assert(try! todo.getLast() == "Test liveness after being demoted from default")
+
+try! todo2.addItem(todo: "Test shared state through local vs default reference")
+assert(try! getDefaultList()!.getLast() == "Test shared state through local vs default reference")
+
diff --git a/toolkit/components/uniffi-fixtures/todolist/tests/test_generated_bindings.rs b/toolkit/components/uniffi-fixtures/todolist/tests/test_generated_bindings.rs
new file mode 100644
index 0000000000..cefdbfe1dc
--- /dev/null
+++ b/toolkit/components/uniffi-fixtures/todolist/tests/test_generated_bindings.rs
@@ -0,0 +1,6 @@
+uniffi::build_foreign_language_testcases!(
+ "tests/bindings/test_todolist.kts",
+ "tests/bindings/test_todolist.swift",
+ "tests/bindings/test_todolist.rb",
+ "tests/bindings/test_todolist.py"
+);