-
recent
-
+
diff --git a/apps/extension/src/api.ts b/apps/extension/src/api.ts
index 1f86ed2..d1d638f 100644
--- a/apps/extension/src/api.ts
+++ b/apps/extension/src/api.ts
@@ -1,4 +1,6 @@
-const baseurl = "https://noro.sh/api";
+import { baseurl } from "./constants";
+
+export { baseurl };
export interface StoreResponse {
id: string;
diff --git a/apps/extension/src/autofill.ts b/apps/extension/src/autofill.ts
new file mode 100644
index 0000000..71e59c7
--- /dev/null
+++ b/apps/extension/src/autofill.ts
@@ -0,0 +1,355 @@
+import type { Credential, VaultItem } from "./types";
+import type { LoginForm } from "./detector";
+import { setvalue } from "./detector";
+import { generatepassword } from "./crypto";
+
+type CredentialLike = Credential | VaultItem;
+
+let popup: HTMLDivElement | null = null;
+
+export function createpopup(
+ credentials: CredentialLike[],
+ anchor: HTMLInputElement,
+ onselect: (cred: CredentialLike) => void,
+ onsave: () => void,
+ ongenerate?: (password: string) => void
+): void {
+ removepopup();
+
+ popup = document.createElement("div");
+ popup.id = "noro-autofill";
+
+ const style = document.createElement("style");
+ style.textContent = `
+ #noro-autofill {
+ position: fixed;
+ z-index: 2147483647;
+ background: #1a1a1a;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 8px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
+ font-family: ui-monospace, monospace;
+ font-size: 13px;
+ color: #fff;
+ min-width: 240px;
+ max-width: 320px;
+ overflow: hidden;
+ }
+ #noro-autofill .noro-header {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 10px 12px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
+ color: #d4b08c;
+ font-size: 11px;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ }
+ #noro-autofill .noro-list {
+ max-height: 200px;
+ overflow-y: auto;
+ }
+ #noro-autofill .noro-item {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px 12px;
+ cursor: pointer;
+ transition: background 0.1s;
+ border: none;
+ background: transparent;
+ width: 100%;
+ text-align: left;
+ }
+ #noro-autofill .noro-item:hover {
+ background: rgba(255, 255, 255, 0.05);
+ }
+ #noro-autofill .noro-icon {
+ width: 28px;
+ height: 28px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(212, 176, 140, 0.15);
+ border-radius: 6px;
+ color: #d4b08c;
+ font-size: 12px;
+ flex-shrink: 0;
+ }
+ #noro-autofill .noro-info {
+ flex: 1;
+ min-width: 0;
+ }
+ #noro-autofill .noro-title {
+ font-size: 13px;
+ color: #fff;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ #noro-autofill .noro-subtitle {
+ font-size: 11px;
+ color: rgba(255, 255, 255, 0.4);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ #noro-autofill .noro-actions {
+ display: flex;
+ gap: 4px;
+ }
+ #noro-autofill .noro-btn {
+ width: 24px;
+ height: 24px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(255, 255, 255, 0.08);
+ border: none;
+ border-radius: 4px;
+ color: rgba(255, 255, 255, 0.5);
+ cursor: pointer;
+ transition: all 0.1s;
+ }
+ #noro-autofill .noro-btn:hover {
+ background: rgba(212, 176, 140, 0.2);
+ color: #d4b08c;
+ }
+ #noro-autofill .noro-empty {
+ padding: 20px 12px;
+ text-align: center;
+ color: rgba(255, 255, 255, 0.4);
+ font-size: 12px;
+ }
+ #noro-autofill .noro-generate {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px 12px;
+ border-top: 1px solid rgba(255, 255, 255, 0.08);
+ cursor: pointer;
+ transition: background 0.1s;
+ }
+ #noro-autofill .noro-generate:hover {
+ background: rgba(255, 255, 255, 0.05);
+ }
+ #noro-autofill .noro-save {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px 12px;
+ cursor: pointer;
+ transition: background 0.1s;
+ }
+ #noro-autofill .noro-save:hover {
+ background: rgba(255, 255, 255, 0.05);
+ }
+ `;
+ popup.appendChild(style);
+
+ const header = document.createElement("div");
+ header.className = "noro-header";
+ header.innerHTML = `
noro`;
+ popup.appendChild(header);
+
+ const list = document.createElement("div");
+ list.className = "noro-list";
+
+ if (credentials.length === 0) {
+ const empty = document.createElement("div");
+ empty.className = "noro-empty";
+ empty.textContent = "no saved logins for this site";
+ list.appendChild(empty);
+ } else {
+ for (const cred of credentials) {
+ const item = document.createElement("button");
+ item.className = "noro-item";
+
+ const icon = document.createElement("div");
+ icon.className = "noro-icon";
+ icon.textContent = "K";
+
+ const info = document.createElement("div");
+ info.className = "noro-info";
+
+ const title = document.createElement("div");
+ title.className = "noro-title";
+ title.textContent = "title" in cred ? cred.title : cred.site;
+
+ const subtitle = document.createElement("div");
+ subtitle.className = "noro-subtitle";
+ subtitle.textContent = cred.username || "";
+
+ info.appendChild(title);
+ info.appendChild(subtitle);
+
+ const actions = document.createElement("div");
+ actions.className = "noro-actions";
+
+ const copybtn = document.createElement("button");
+ copybtn.className = "noro-btn";
+ copybtn.innerHTML = `
`;
+ copybtn.addEventListener("click", async (e) => {
+ e.stopPropagation();
+ if (cred.password) {
+ await navigator.clipboard.writeText(cred.password);
+ }
+ });
+
+ actions.appendChild(copybtn);
+
+ item.appendChild(icon);
+ item.appendChild(info);
+ item.appendChild(actions);
+
+ item.addEventListener("click", () => {
+ onselect(cred);
+ removepopup();
+ });
+
+ list.appendChild(item);
+ }
+ }
+
+ popup.appendChild(list);
+
+ if (credentials.length === 0) {
+ const save = document.createElement("div");
+ save.className = "noro-save";
+ save.innerHTML = `
+
+
+
save after login
+
capture credentials on submit
+
+ `;
+ save.addEventListener("click", () => {
+ onsave();
+ removepopup();
+ });
+ popup.appendChild(save);
+ }
+
+ if (ongenerate) {
+ const generate = document.createElement("div");
+ generate.className = "noro-generate";
+ generate.innerHTML = `
+
+
+
generate password
+
create strong password
+
+ `;
+ generate.addEventListener("click", () => {
+ const password = generatepassword();
+ ongenerate(password);
+ removepopup();
+ });
+ popup.appendChild(generate);
+ }
+
+ document.body.appendChild(popup);
+ positionpopup(anchor);
+
+ const close = (e: MouseEvent) => {
+ if (popup && !popup.contains(e.target as Node)) {
+ removepopup();
+ document.removeEventListener("click", close);
+ }
+ };
+ setTimeout(() => document.addEventListener("click", close), 0);
+
+ document.addEventListener("keydown", handleescape);
+}
+
+function positionpopup(anchor: HTMLInputElement): void {
+ if (!popup) return;
+
+ const rect = anchor.getBoundingClientRect();
+ popup.style.top = `${rect.bottom + 4}px`;
+ popup.style.left = `${rect.left}px`;
+
+ const popuprect = popup.getBoundingClientRect();
+
+ if (popuprect.bottom > window.innerHeight) {
+ popup.style.top = `${rect.top - popuprect.height - 4}px`;
+ }
+
+ if (popuprect.right > window.innerWidth) {
+ popup.style.left = `${window.innerWidth - popuprect.width - 8}px`;
+ }
+}
+
+function handleescape(e: KeyboardEvent): void {
+ if (e.key === "Escape") {
+ removepopup();
+ }
+}
+
+export function removepopup(): void {
+ if (popup) {
+ popup.remove();
+ popup = null;
+ }
+ document.removeEventListener("keydown", handleescape);
+}
+
+export function createicon(onclick: () => void): HTMLDivElement {
+ const icon = document.createElement("div");
+ icon.style.cssText = `
+ position: absolute;
+ right: 8px;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 20px;
+ height: 20px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(212, 176, 140, 0.15);
+ border-radius: 4px;
+ cursor: pointer;
+ z-index: 10000;
+ transition: background 0.1s;
+ `;
+ icon.innerHTML = `
`;
+
+ icon.addEventListener("mouseenter", () => {
+ icon.style.background = "rgba(212, 176, 140, 0.25)";
+ });
+ icon.addEventListener("mouseleave", () => {
+ icon.style.background = "rgba(212, 176, 140, 0.15)";
+ });
+ icon.addEventListener("click", (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ onclick();
+ });
+
+ return icon;
+}
+
+export function fill(form: LoginForm, cred: { username?: string; password?: string }): void {
+ if (cred.username) {
+ setvalue(form.username, cred.username);
+ }
+ if (cred.password) {
+ setvalue(form.password, cred.password);
+ }
+}
+
+export function fillpassword(form: LoginForm, password: string): void {
+ setvalue(form.password, password);
+}
diff --git a/apps/extension/src/background.ts b/apps/extension/src/background.ts
index 6ddf55a..f566bd9 100644
--- a/apps/extension/src/background.ts
+++ b/apps/extension/src/background.ts
@@ -1,5 +1,12 @@
import { encrypt, generatekey } from "./crypto";
import { store } from "./api";
+import { baseurl } from "./constants";
+import { getcredentials, savecredential } from "./credentials";
+import { matchurl, fetchitems } from "./vault";
+import type { VaultItem, RecentSecret } from "./types";
+
+let cachedvault: VaultItem[] = [];
+let vaultexpiry = 0;
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
@@ -37,19 +44,32 @@ chrome.contextMenus.onClicked.addListener(async (info) => {
}
});
-interface RecentSecret {
- id: string;
- url: string;
- preview: string;
- created: number;
-}
-
async function saverecent(secret: RecentSecret) {
const { recents = [] } = await chrome.storage.local.get("recents");
const updated = [secret, ...recents.slice(0, 9)];
await chrome.storage.local.set({ recents: updated });
}
+async function fetchvaultitems(): Promise
{
+ const now = Date.now();
+ if (cachedvault.length > 0 && now < vaultexpiry) {
+ return cachedvault;
+ }
+
+ const result = await fetchitems();
+ if (result.success && result.items) {
+ cachedvault = result.items;
+ vaultexpiry = now + 60000;
+ return cachedvault;
+ }
+ return [];
+}
+
+function clearvaultcache() {
+ cachedvault = [];
+ vaultexpiry = 0;
+}
+
chrome.runtime.onMessage.addListener((message, _, respond) => {
if (message.type === "share") {
(async () => {
@@ -73,4 +93,97 @@ chrome.runtime.onMessage.addListener((message, _, respond) => {
});
return true;
}
+
+ if (message.type === "login") {
+ (async () => {
+ try {
+ const res = await fetch(`${baseurl}/auth/sign-in/email`, {
+ method: "POST",
+ headers: { "content-type": "application/json" },
+ body: JSON.stringify({
+ email: message.email,
+ password: message.password,
+ }),
+ });
+
+ if (!res.ok) {
+ const data = await res.json().catch(() => ({}));
+ respond({ success: false, error: data.message || "invalid credentials" });
+ return;
+ }
+
+ const data = await res.json();
+ clearvaultcache();
+ respond({ success: true, token: data.token || "session" });
+ } catch {
+ respond({ success: false, error: "connection failed" });
+ }
+ })();
+ return true;
+ }
+
+ if (message.type === "items") {
+ (async () => {
+ try {
+ const items = await fetchvaultitems();
+ respond({ success: true, items });
+ } catch {
+ respond({ success: false, error: "connection failed" });
+ }
+ })();
+ return true;
+ }
+
+ if (message.type === "vaultmatch") {
+ (async () => {
+ try {
+ const items = await fetchvaultitems();
+ const matched = items.filter((item) => item.type === "login" && matchurl(item.url, message.url));
+ respond(matched);
+ } catch {
+ respond([]);
+ }
+ })();
+ return true;
+ }
+
+ if (message.type === "credentials") {
+ getcredentials(message.site).then(respond);
+ return true;
+ }
+
+ if (message.type === "savecredential") {
+ savecredential(message.site, message.username, message.password).then(() => {
+ chrome.notifications.create({
+ type: "basic",
+ iconUrl: "icons/128.png",
+ title: "noro",
+ message: "login saved",
+ });
+ respond({ success: true });
+ });
+ return true;
+ }
+
+ if (message.type === "autofill") {
+ chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
+ if (tabs[0]?.id) {
+ chrome.tabs.sendMessage(tabs[0].id, { type: "autofill", item: message.item });
+ }
+ });
+ respond({ success: true });
+ return true;
+ }
+
+ if (message.type === "clearcache") {
+ clearvaultcache();
+ respond({ success: true });
+ return true;
+ }
+});
+
+chrome.commands.onCommand.addListener((command) => {
+ if (command === "open-popup") {
+ chrome.action.openPopup();
+ }
});
diff --git a/apps/extension/src/constants.ts b/apps/extension/src/constants.ts
new file mode 100644
index 0000000..37960c8
--- /dev/null
+++ b/apps/extension/src/constants.ts
@@ -0,0 +1 @@
+export const baseurl = "https://noro.sh/api";
diff --git a/apps/extension/src/content.ts b/apps/extension/src/content.ts
new file mode 100644
index 0000000..efd6154
--- /dev/null
+++ b/apps/extension/src/content.ts
@@ -0,0 +1,125 @@
+import { createpopup, createicon, fill, fillpassword } from "./autofill";
+import { findloginforms, getsite, observeforms } from "./detector";
+import type { LoginForm } from "./detector";
+import type { Credential, VaultItem } from "./types";
+
+type CredentialOrVaultItem = Credential | VaultItem;
+
+let currentform: LoginForm | null = null;
+
+async function getcredentials(): Promise {
+ return new Promise((resolve) => {
+ chrome.runtime.sendMessage({ type: "credentials", site: getsite() }, (response) => {
+ resolve(response || []);
+ });
+ });
+}
+
+async function getvaultitems(): Promise {
+ return new Promise((resolve) => {
+ chrome.runtime.sendMessage({ type: "vaultmatch", url: window.location.href }, (response) => {
+ resolve(response || []);
+ });
+ });
+}
+
+function enablesaveonsubmit(): void {
+ if (!currentform) return;
+
+ const { form, username, password } = currentform;
+
+ const save = () => {
+ const user = username.value.trim();
+ const pass = password.value;
+
+ if (user && pass) {
+ chrome.runtime.sendMessage({
+ type: "savecredential",
+ site: getsite(),
+ username: user,
+ password: pass,
+ });
+ }
+ };
+
+ if (form) {
+ form.addEventListener("submit", save, { once: true });
+ }
+
+ const buttons = document.querySelectorAll(
+ 'button[type="submit"], input[type="submit"], button:not([type])'
+ );
+ for (const button of buttons) {
+ button.addEventListener("click", () => setTimeout(save, 100), { once: true });
+ }
+}
+
+function handleselect(cred: CredentialOrVaultItem): void {
+ if (!currentform) return;
+ fill(currentform, cred);
+}
+
+function handlegenerate(password: string): void {
+ if (!currentform) return;
+ fillpassword(currentform, password);
+}
+
+function addicons(): void {
+ const forms = findloginforms();
+
+ for (const loginform of forms) {
+ if (loginform.username.dataset.noroicon) continue;
+
+ loginform.username.dataset.noroicon = "true";
+ loginform.password.dataset.noroicon = "true";
+
+ const wrapper = loginform.username.parentElement;
+ if (wrapper) {
+ const computed = getComputedStyle(wrapper);
+ if (computed.position === "static") {
+ wrapper.style.position = "relative";
+ }
+
+ const icon = createicon(async () => {
+ currentform = loginform;
+ const [credentials, vaultitems] = await Promise.all([
+ getcredentials(),
+ getvaultitems(),
+ ]);
+ const combined: CredentialOrVaultItem[] = [...vaultitems, ...credentials];
+ createpopup(combined, loginform.username, handleselect, enablesaveonsubmit, handlegenerate);
+ });
+
+ wrapper.appendChild(icon);
+ }
+ }
+}
+
+function init(): void {
+ addicons();
+ observeforms(addicons);
+}
+
+chrome.runtime.onMessage.addListener((message, _, respond) => {
+ if (message.type === "autofill") {
+ const forms = findloginforms();
+ if (forms.length > 0 && message.item) {
+ currentform = forms[0];
+ fill(currentform, message.item);
+ }
+ respond({ success: true });
+ return true;
+ }
+
+ if (message.type === "getforms") {
+ const forms = findloginforms();
+ respond({ hasforms: forms.length > 0 });
+ return true;
+ }
+});
+
+if (document.readyState === "loading") {
+ document.addEventListener("DOMContentLoaded", init);
+} else {
+ init();
+}
diff --git a/apps/extension/src/credentials.ts b/apps/extension/src/credentials.ts
new file mode 100644
index 0000000..4dbcfe9
--- /dev/null
+++ b/apps/extension/src/credentials.ts
@@ -0,0 +1,33 @@
+import type { Credential } from "./types";
+
+export type { Credential };
+
+export { getsession, setsession } from "./session";
+
+export async function getcredentials(site: string): Promise {
+ const { credentials = [] } = await chrome.storage.local.get("credentials");
+ return credentials.filter((c: Credential) => c.site === site || site.endsWith("." + c.site));
+}
+
+export async function savecredential(site: string, username: string, password: string): Promise {
+ const { credentials = [] } = await chrome.storage.local.get("credentials");
+ const existing = credentials.findIndex(
+ (c: Credential) => c.site === site && c.username === username
+ );
+
+ const cred: Credential = {
+ id: crypto.randomUUID(),
+ site,
+ username,
+ password,
+ created: Date.now(),
+ };
+
+ if (existing >= 0) {
+ credentials[existing] = cred;
+ } else {
+ credentials.unshift(cred);
+ }
+
+ await chrome.storage.local.set({ credentials });
+}
diff --git a/apps/extension/src/crypto.ts b/apps/extension/src/crypto.ts
index 31053ae..45299bc 100644
--- a/apps/extension/src/crypto.ts
+++ b/apps/extension/src/crypto.ts
@@ -1,40 +1,88 @@
-export function generatekey(): string {
- const bytes = crypto.getRandomValues(new Uint8Array(24));
- let key = "";
- const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
- for (let i = 0; i < 24; i++) {
- key += chars[bytes[i] % chars.length];
- }
- return key;
+async function derivekey(key: string, usage: "encrypt" | "decrypt") {
+ const keydata = new TextEncoder().encode(key.padEnd(32, "0").slice(0, 32));
+ return crypto.subtle.importKey("raw", keydata, "AES-GCM", false, [usage]);
+}
+
+function tobase64url(bytes: Uint8Array): string {
+ let binary = "";
+ bytes.forEach((byte) => (binary += String.fromCharCode(byte)));
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
+}
+
+function frombase64url(encoded: string): Uint8Array {
+ const base64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
+ const padding = (4 - (base64.length % 4)) % 4;
+ const padded = base64 + "=".repeat(padding);
+ const binary = atob(padded);
+ const bytes = new Uint8Array(binary.length);
+ for (let i = 0; i < binary.length; i++) {
+ bytes[i] = binary.charCodeAt(i);
+ }
+ return bytes;
}
export async function encrypt(text: string, key: string): Promise {
- const encoder = new TextEncoder();
- const data = encoder.encode(text);
- const keydata = encoder.encode(key.padEnd(32, "0").slice(0, 32));
- const iv = crypto.getRandomValues(new Uint8Array(12));
- const cryptokey = await crypto.subtle.importKey("raw", keydata, "AES-GCM", false, ["encrypt"]);
- const encrypted = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, cryptokey, data);
- const combined = new Uint8Array(iv.length + encrypted.byteLength);
- combined.set(iv);
- combined.set(new Uint8Array(encrypted), iv.length);
- return btoa(String.fromCharCode(...combined))
- .replace(/\+/g, "-")
- .replace(/\//g, "_")
- .replace(/=+$/, "");
+ const data = new TextEncoder().encode(text);
+ const iv = crypto.getRandomValues(new Uint8Array(12));
+ const cryptokey = await derivekey(key, "encrypt");
+ const encrypted = await crypto.subtle.encrypt(
+ { name: "AES-GCM", iv },
+ cryptokey,
+ data
+ );
+ const combined = new Uint8Array(iv.length + encrypted.byteLength);
+ combined.set(iv);
+ combined.set(new Uint8Array(encrypted), iv.length);
+ return tobase64url(combined);
}
export async function decrypt(encoded: string, key: string): Promise {
- const base64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
- const binary = atob(base64);
- const bytes = new Uint8Array(binary.length);
- for (let i = 0; i < binary.length; i++) {
- bytes[i] = binary.charCodeAt(i);
- }
- const iv = bytes.slice(0, 12);
- const data = bytes.slice(12);
- const keydata = new TextEncoder().encode(key.padEnd(32, "0").slice(0, 32));
- const cryptokey = await crypto.subtle.importKey("raw", keydata, "AES-GCM", false, ["decrypt"]);
- const decrypted = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, cryptokey, data);
- return new TextDecoder().decode(decrypted);
+ const bytes = frombase64url(encoded);
+ const iv = bytes.slice(0, 12);
+ const data = bytes.slice(12);
+ const cryptokey = await derivekey(key, "decrypt");
+ const decrypted = await crypto.subtle.decrypt(
+ { name: "AES-GCM", iv },
+ cryptokey,
+ data
+ );
+ return new TextDecoder().decode(decrypted);
+}
+
+export function generatekey(): string {
+ const bytes = crypto.getRandomValues(new Uint8Array(24));
+ let key = "";
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
+ for (let i = 0; i < 24; i++) {
+ key += chars[bytes[i] % chars.length];
+ }
+ return key;
+}
+
+export function generatepassword(length = 20): string {
+ const lower = "abcdefghijklmnopqrstuvwxyz";
+ const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ const digits = "0123456789";
+ const symbols = "!@#$%^&*";
+ const all = lower + upper + digits + symbols;
+
+ const bytes = crypto.getRandomValues(new Uint8Array(length));
+ let password = "";
+
+ password += lower[bytes[0] % lower.length];
+ password += upper[bytes[1] % upper.length];
+ password += digits[bytes[2] % digits.length];
+ password += symbols[bytes[3] % symbols.length];
+
+ for (let i = 4; i < length; i++) {
+ password += all[bytes[i] % all.length];
+ }
+
+ const shuffled = password.split("");
+ for (let i = shuffled.length - 1; i > 0; i--) {
+ const j = bytes[i % bytes.length] % (i + 1);
+ [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
+ }
+
+ return shuffled.join("");
}
diff --git a/apps/extension/src/detector.ts b/apps/extension/src/detector.ts
new file mode 100644
index 0000000..033f78a
--- /dev/null
+++ b/apps/extension/src/detector.ts
@@ -0,0 +1,77 @@
+export interface LoginForm {
+ form: HTMLFormElement | null;
+ username: HTMLInputElement;
+ password: HTMLInputElement;
+}
+
+export function findloginforms(): LoginForm[] {
+ const forms: LoginForm[] = [];
+ const passwords = document.querySelectorAll('input[type="password"]');
+
+ for (const password of passwords) {
+ if (!password.offsetParent) continue;
+
+ const form = password.closest("form");
+ const container = form || password.parentElement;
+ if (!container) continue;
+
+ const inputs = container.querySelectorAll("input");
+ let username: HTMLInputElement | null = null;
+
+ for (const input of inputs) {
+ if (input === password) continue;
+ if (input.type === "hidden" || input.type === "submit") continue;
+
+ const type = input.type.toLowerCase();
+ const name = (input.name || "").toLowerCase();
+ const id = (input.id || "").toLowerCase();
+ const autocomplete = (input.autocomplete || "").toLowerCase();
+
+ if (
+ type === "email" ||
+ type === "text" ||
+ name.includes("user") ||
+ name.includes("email") ||
+ name.includes("login") ||
+ id.includes("user") ||
+ id.includes("email") ||
+ id.includes("login") ||
+ autocomplete === "username" ||
+ autocomplete === "email"
+ ) {
+ username = input;
+ break;
+ }
+ }
+
+ if (username) {
+ forms.push({ form, username, password });
+ }
+ }
+
+ return forms;
+}
+
+export function getsite(): string {
+ return window.location.hostname.replace(/^www\./, "");
+}
+
+export function setvalue(input: HTMLInputElement, value: string): void {
+ input.focus();
+ input.value = value;
+ input.dispatchEvent(new Event("input", { bubbles: true }));
+ input.dispatchEvent(new Event("change", { bubbles: true }));
+}
+
+export function observeforms(callback: () => void): MutationObserver {
+ const observer = new MutationObserver(() => {
+ callback();
+ });
+
+ observer.observe(document.body, {
+ childList: true,
+ subtree: true,
+ });
+
+ return observer;
+}
diff --git a/apps/extension/src/popup.ts b/apps/extension/src/popup.ts
index 5618df0..6634ae4 100644
--- a/apps/extension/src/popup.ts
+++ b/apps/extension/src/popup.ts
@@ -1,97 +1,307 @@
-const secret = document.getElementById("secret") as HTMLTextAreaElement;
-const ttl = document.getElementById("ttl") as HTMLSelectElement;
-const views = document.getElementById("views") as HTMLSelectElement;
-const share = document.getElementById("share") as HTMLButtonElement;
-const url = document.getElementById("url") as HTMLInputElement;
-const copy = document.getElementById("copy") as HTMLButtonElement;
-const newbtn = document.getElementById("new") as HTMLButtonElement;
-const list = document.getElementById("list") as HTMLUListElement;
-const createview = document.getElementById("create") as HTMLDivElement;
-const resultview = document.getElementById("result") as HTMLDivElement;
-
-function show(view: "create" | "result") {
- createview.classList.toggle("active", view === "create");
- resultview.classList.toggle("active", view === "result");
-}
+import type { VaultItem } from "./types";
+import { getsession, setsession } from "./session";
-share.addEventListener("click", async () => {
- const text = secret.value.trim();
- if (!text) return;
+const loginview = document.getElementById("login") as HTMLDivElement;
+const vaultview = document.getElementById("vault") as HTMLDivElement;
+const detailview = document.getElementById("detail") as HTMLDivElement;
- share.disabled = true;
- share.textContent = "sharing...";
+const emailinput = document.getElementById("email") as HTMLInputElement;
+const passwordinput = document.getElementById("password") as HTMLInputElement;
+const loginerror = document.getElementById("loginerror") as HTMLParagraphElement;
+const signinbtn = document.getElementById("signin") as HTMLButtonElement;
- const response = await chrome.runtime.sendMessage({
- type: "share",
- text,
- ttl: ttl.value,
- views: parseInt(views.value),
- });
+const searchinput = document.getElementById("search") as HTMLInputElement;
+const itemslist = document.getElementById("items") as HTMLUListElement;
+const useremail = document.getElementById("useremail") as HTMLSpanElement;
+const signoutbtn = document.getElementById("signout") as HTMLButtonElement;
- if (response.success) {
- url.value = response.url;
- show("result");
- loadrecents();
+const backbtn = document.getElementById("back") as HTMLButtonElement;
+const detailicon = document.getElementById("detailicon") as HTMLSpanElement;
+const detailtitle = document.getElementById("detailtitle") as HTMLHeadingElement;
+const detailtype = document.getElementById("detailtype") as HTMLParagraphElement;
+const fieldscontainer = document.getElementById("fields") as HTMLDivElement;
+
+let items: VaultItem[] = [];
+let currentitem: VaultItem | null = null;
+
+const icons: Record = {
+ login: "K",
+ note: "N",
+ card: "C",
+ identity: "I",
+ ssh: "S",
+ api: "A",
+};
+
+function show(view: "login" | "vault" | "detail") {
+ loginview.classList.toggle("active", view === "login");
+ vaultview.classList.toggle("active", view === "vault");
+ detailview.classList.toggle("active", view === "detail");
+}
+
+async function init() {
+ const session = await getsession();
+ if (session) {
+ useremail.textContent = session.email;
+ await loaditems();
+ show("vault");
} else {
- share.textContent = "failed";
- setTimeout(() => {
- share.textContent = "share";
- share.disabled = false;
- }, 2000);
+ show("login");
}
-});
+}
+
+signinbtn.addEventListener("click", async () => {
+ const email = emailinput.value.trim();
+ const password = passwordinput.value;
+
+ if (!email || !password) {
+ loginerror.textContent = "all fields required";
+ return;
+ }
+
+ loginerror.textContent = "";
+ signinbtn.disabled = true;
+ signinbtn.textContent = "signing in...";
+
+ try {
+ const response = await chrome.runtime.sendMessage({
+ type: "login",
+ email,
+ password,
+ });
-copy.addEventListener("click", async () => {
- await navigator.clipboard.writeText(url.value);
- copy.textContent = "copied!";
- setTimeout(() => {
- copy.textContent = "copy";
- }, 2000);
+ if (response.success) {
+ await setsession({ email, token: response.token });
+ useremail.textContent = email;
+ await loaditems();
+ show("vault");
+ emailinput.value = "";
+ passwordinput.value = "";
+ } else {
+ loginerror.textContent = response.error || "login failed";
+ }
+ } catch {
+ loginerror.textContent = "connection error";
+ } finally {
+ signinbtn.disabled = false;
+ signinbtn.textContent = "sign in";
+ }
});
-newbtn.addEventListener("click", () => {
- secret.value = "";
- share.textContent = "share";
- share.disabled = false;
- show("create");
+signoutbtn.addEventListener("click", async () => {
+ await setsession(null);
+ await chrome.runtime.sendMessage({ type: "clearcache" });
+ items = [];
+ searchinput.value = "";
+ show("login");
});
-interface RecentSecret {
- id: string;
- url: string;
- preview: string;
- created: number;
+async function loaditems() {
+ const response = await chrome.runtime.sendMessage({ type: "items" });
+ if (response.success) {
+ items = response.items;
+ renderitems();
+ }
}
-async function loadrecents() {
- const recents: RecentSecret[] = await chrome.runtime.sendMessage({ type: "recents" });
- list.innerHTML = "";
+function renderitems() {
+ const query = searchinput.value.toLowerCase();
+ const filtered = items.filter(
+ (item) =>
+ item.title.toLowerCase().includes(query) ||
+ (item.username && item.username.toLowerCase().includes(query)) ||
+ (item.url && item.url.toLowerCase().includes(query)),
+ );
- if (recents.length === 0) {
- list.innerHTML = "no recent secrets";
+ itemslist.innerHTML = "";
+
+ if (filtered.length === 0) {
+ const empty = document.createElement("li");
+ empty.className = "empty";
+ const icon = document.createElement("div");
+ icon.className = "emptyicon";
+ icon.innerHTML = items.length === 0
+ ? ``
+ : ``;
+ const text = document.createElement("span");
+ text.textContent = items.length === 0 ? "your vault is empty" : "no matches found";
+ empty.appendChild(icon);
+ empty.appendChild(text);
+ itemslist.appendChild(empty);
return;
}
- for (const item of recents) {
+ const sorted = [...filtered].sort((a, b) => {
+ if (a.favorite !== b.favorite) return a.favorite ? -1 : 1;
+ return a.title.localeCompare(b.title);
+ });
+
+ for (const item of sorted) {
const li = document.createElement("li");
- const preview = document.createElement("span");
- preview.className = "preview";
- preview.textContent = item.preview + (item.preview.length >= 50 ? "..." : "");
-
- const copybtn = document.createElement("button");
- copybtn.textContent = "copy";
- copybtn.addEventListener("click", async () => {
- await navigator.clipboard.writeText(item.url);
- copybtn.textContent = "copied!";
- setTimeout(() => {
- copybtn.textContent = "copy";
- }, 2000);
+ li.addEventListener("click", () => showdetail(item));
+
+ const icon = document.createElement("span");
+ icon.className = "icon";
+ icon.textContent = icons[item.type] || "?";
+
+ const info = document.createElement("div");
+ info.className = "info";
+
+ const title = document.createElement("div");
+ title.className = "title";
+ title.textContent = item.title;
+
+ const subtitle = document.createElement("div");
+ subtitle.className = "subtitle";
+ subtitle.textContent = item.username || item.type;
+
+ info.appendChild(title);
+ info.appendChild(subtitle);
+
+ li.appendChild(icon);
+ li.appendChild(info);
+
+ if (item.favorite) {
+ const star = document.createElement("span");
+ star.className = "star";
+ star.textContent = "*";
+ li.appendChild(star);
+ }
+
+ itemslist.appendChild(li);
+ }
+}
+
+searchinput.addEventListener("input", renderitems);
+
+function showdetail(item: VaultItem) {
+ currentitem = item;
+ detailicon.textContent = icons[item.type] || "?";
+ detailtitle.textContent = item.title;
+ detailtype.textContent = item.type;
+
+ fieldscontainer.innerHTML = "";
+
+ if (item.username) {
+ fieldscontainer.appendChild(createfield("username", item.username, false));
+ }
+
+ if (item.password) {
+ fieldscontainer.appendChild(createfield("password", item.password, true));
+ }
+
+ if (item.url) {
+ fieldscontainer.appendChild(createfield("url", item.url, false, true));
+ }
+
+ if (item.notes) {
+ fieldscontainer.appendChild(createfield("notes", item.notes, false));
+ }
+
+ if (item.type === "login" && (item.username || item.password)) {
+ fieldscontainer.appendChild(createautofillbtn(item));
+ }
+
+ show("detail");
+}
+
+function createfield(
+ label: string,
+ value: string,
+ ispassword: boolean,
+ isurl = false,
+): HTMLDivElement {
+ const field = document.createElement("div");
+ field.className = "field";
+
+ const labelel = document.createElement("div");
+ labelel.className = "label";
+ labelel.textContent = label;
+
+ const valueel = document.createElement("div");
+ valueel.className = "value";
+
+ const textel = document.createElement("span");
+ textel.className = ispassword ? "text password" : "text";
+
+ let hidden = ispassword;
+ const displayvalue = () => {
+ textel.textContent = hidden ? "••••••••••" : value;
+ };
+ displayvalue();
+
+ const actions = document.createElement("div");
+ actions.className = "actions";
+
+ const copybtn = document.createElement("button");
+ copybtn.type = "button";
+ copybtn.innerHTML = ``;
+ copybtn.addEventListener("click", async () => {
+ await navigator.clipboard.writeText(value);
+ copybtn.innerHTML = ``;
+ setTimeout(() => {
+ copybtn.innerHTML = ``;
+ }, 1500);
+ });
+
+ actions.appendChild(copybtn);
+
+ if (ispassword) {
+ const togglebtn = document.createElement("button");
+ togglebtn.type = "button";
+ togglebtn.innerHTML = ``;
+ togglebtn.addEventListener("click", () => {
+ hidden = !hidden;
+ displayvalue();
+ togglebtn.innerHTML = hidden
+ ? ``
+ : ``;
});
+ actions.appendChild(togglebtn);
+ }
- li.appendChild(preview);
- li.appendChild(copybtn);
- list.appendChild(li);
+ if (isurl) {
+ const openbtn = document.createElement("button");
+ openbtn.type = "button";
+ openbtn.innerHTML = ``;
+ openbtn.addEventListener("click", () => {
+ let url = value;
+ if (!url.startsWith("http://") && !url.startsWith("https://")) {
+ url = "https://" + url;
+ }
+ chrome.tabs.create({ url });
+ });
+ actions.appendChild(openbtn);
}
+
+ valueel.appendChild(textel);
+ valueel.appendChild(actions);
+ field.appendChild(labelel);
+ field.appendChild(valueel);
+
+ return field;
+}
+
+function createautofillbtn(item: VaultItem): HTMLDivElement {
+ const wrapper = document.createElement("div");
+ wrapper.className = "field autofill";
+
+ const btn = document.createElement("button");
+ btn.type = "button";
+ btn.className = "autofillbtn";
+ btn.innerHTML = `autofill on page`;
+ btn.addEventListener("click", async () => {
+ await chrome.runtime.sendMessage({ type: "autofill", item });
+ window.close();
+ });
+
+ wrapper.appendChild(btn);
+ return wrapper;
}
-loadrecents();
+backbtn.addEventListener("click", () => {
+ currentitem = null;
+ show("vault");
+});
+
+init();
diff --git a/apps/extension/src/session.ts b/apps/extension/src/session.ts
new file mode 100644
index 0000000..2c2f30e
--- /dev/null
+++ b/apps/extension/src/session.ts
@@ -0,0 +1,14 @@
+import type { Session } from "./types";
+
+export async function getsession(): Promise {
+ const { session } = await chrome.storage.local.get("session");
+ return session || null;
+}
+
+export async function setsession(session: Session | null): Promise {
+ if (session) {
+ await chrome.storage.local.set({ session });
+ } else {
+ await chrome.storage.local.remove("session");
+ }
+}
diff --git a/apps/extension/src/types.ts b/apps/extension/src/types.ts
new file mode 100644
index 0000000..9d75378
--- /dev/null
+++ b/apps/extension/src/types.ts
@@ -0,0 +1,30 @@
+export interface Credential {
+ id: string;
+ site: string;
+ username: string;
+ password: string;
+ created: number;
+}
+
+export interface Session {
+ email: string;
+ token: string;
+}
+
+export interface VaultItem {
+ id: string;
+ type: string;
+ title: string;
+ username?: string;
+ password?: string;
+ url?: string;
+ notes?: string;
+ favorite: boolean;
+}
+
+export interface RecentSecret {
+ id: string;
+ url: string;
+ preview: string;
+ created: number;
+}
diff --git a/apps/extension/src/vault.ts b/apps/extension/src/vault.ts
new file mode 100644
index 0000000..9d19a53
--- /dev/null
+++ b/apps/extension/src/vault.ts
@@ -0,0 +1,64 @@
+import type { VaultItem } from "./types";
+import { getsession } from "./session";
+import { baseurl } from "./constants";
+
+export type { VaultItem };
+
+export async function fetchitems(): Promise<{ success: boolean; items?: VaultItem[]; error?: string }> {
+ const session = await getsession();
+ if (!session) {
+ return { success: false, error: "not authenticated" };
+ }
+
+ try {
+ const res = await fetch(`${baseurl}/v1/vault/items`, {
+ headers: { authorization: `Bearer ${session.token}` },
+ });
+
+ if (!res.ok) {
+ if (res.status === 401) {
+ await chrome.storage.local.remove("session");
+ return { success: false, error: "session expired" };
+ }
+ return { success: false, error: "failed to load items" };
+ }
+
+ const data = await res.json();
+ const items = (data.items || []).map((item: Record) => {
+ const parsed = typeof item.data === "string" ? JSON.parse(item.data as string) : item.data;
+ return {
+ id: item.id,
+ type: item.type,
+ title: item.title,
+ username: parsed?.username,
+ password: parsed?.password,
+ url: parsed?.url,
+ notes: parsed?.notes,
+ favorite: item.favorite || false,
+ };
+ });
+
+ return { success: true, items };
+ } catch {
+ return { success: false, error: "connection failed" };
+ }
+}
+
+export function matchurl(itemurl: string | undefined, pageurl: string): boolean {
+ if (!itemurl) return false;
+
+ try {
+ let normalized = itemurl;
+ if (!normalized.startsWith("http://") && !normalized.startsWith("https://")) {
+ normalized = "https://" + normalized;
+ }
+
+ const itemhost = new URL(normalized).hostname.replace(/^www\./, "");
+ const pagehost = new URL(pageurl).hostname.replace(/^www\./, "");
+
+ return itemhost === pagehost || pagehost.endsWith("." + itemhost);
+ } catch {
+ return false;
+ }
+}
+
diff --git a/apps/extension/styles.css b/apps/extension/styles.css
index dade2ea..1e62348 100644
--- a/apps/extension/styles.css
+++ b/apps/extension/styles.css
@@ -13,225 +13,536 @@
}
::selection {
- background: #ff6b00;
- color: #000;
+ background: #d4b08c;
+ color: #0a0a0a;
+}
+
+::-webkit-scrollbar {
+ width: 4px;
+}
+
+::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+::-webkit-scrollbar-thumb {
+ background: rgba(255, 255, 255, 0.08);
+ border-radius: 2px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: rgba(255, 255, 255, 0.12);
}
body {
- width: 320px;
- font-family: "JetBrains Mono", ui-monospace, monospace;
+ width: 340px;
+ min-height: 420px;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
font-size: 13px;
- background: oklch(0.145 0 0);
- color: oklch(0.985 0 0);
+ background: #0a0a0a;
+ color: #ededed;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
}
main {
- padding: 16px;
+ padding: 24px;
+ display: flex;
+ flex-direction: column;
+ min-height: 420px;
+ position: relative;
+}
+
+main::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 200px;
+ height: 200px;
+ background: radial-gradient(circle at top right, rgba(212, 176, 140, 0.04) 0%, transparent 70%);
+ pointer-events: none;
}
header {
display: flex;
align-items: center;
- gap: 8px;
- margin-bottom: 16px;
+ gap: 10px;
+ margin-bottom: 28px;
+ position: relative;
+ z-index: 1;
}
header svg {
- color: #ff6b00;
+ color: #d4b08c;
+ opacity: 0.9;
}
header span {
- font-size: 11px;
- font-weight: 700;
- letter-spacing: 0.1em;
- text-transform: uppercase;
- color: rgba(255, 255, 255, 0.6);
+ font-family: Georgia, "Times New Roman", serif;
+ font-style: italic;
+ font-size: 15px;
+ letter-spacing: 0.02em;
+ color: #ededed;
}
.view {
display: none;
+ flex-direction: column;
+ flex: 1;
+ position: relative;
+ z-index: 1;
}
.view.active {
- display: block;
+ display: flex;
}
-textarea {
+input {
width: 100%;
- padding: 12px 16px;
- border: 1px solid rgba(255, 255, 255, 0.1);
- border-radius: 10px;
- background: transparent;
- color: oklch(0.985 0 0);
+ padding: 14px 16px;
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ border-radius: 12px;
+ background: #161616;
+ color: #ededed;
font-family: inherit;
font-size: 13px;
- resize: none;
- transition: border-color 0.15s;
+ transition: all 0.2s ease;
+}
+
+input:hover {
+ border-color: rgba(255, 255, 255, 0.08);
}
-textarea:focus {
+input:focus {
outline: none;
- border-color: #ff6b00;
+ border-color: rgba(212, 176, 140, 0.4);
+ background: #161616;
+ box-shadow: 0 0 0 3px rgba(212, 176, 140, 0.08);
}
-textarea::placeholder {
- color: rgba(255, 255, 255, 0.2);
+input::placeholder {
+ color: rgba(255, 255, 255, 0.25);
}
-.options {
- display: flex;
- gap: 8px;
- margin-top: 12px;
+input.mono {
+ font-family: "JetBrains Mono", ui-monospace, monospace;
+ font-size: 11px;
+ letter-spacing: 0.02em;
}
-select {
- flex: 1;
- padding: 10px 12px;
- border: 1px solid rgba(255, 255, 255, 0.1);
- border-radius: 10px;
- background: transparent;
- color: oklch(0.985 0 0);
- font-family: inherit;
- font-size: 12px;
- cursor: pointer;
- transition: border-color 0.15s;
- appearance: none;
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.4)' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
- background-repeat: no-repeat;
- background-position: right 12px center;
+#login {
+ justify-content: center;
+ padding-top: 16px;
}
-select:focus {
- outline: none;
- border-color: #ff6b00;
+#login input {
+ margin-bottom: 12px;
}
-select option {
- background: oklch(0.145 0 0);
- color: oklch(0.985 0 0);
+.error {
+ color: #ef4444;
+ font-size: 11px;
+ min-height: 18px;
+ margin-bottom: 8px;
+ text-align: center;
+ font-weight: 500;
}
button {
width: 100%;
- padding: 12px 16px;
- margin-top: 12px;
+ padding: 14px 20px;
border: none;
- border-radius: 10px;
- background: #ff6b00;
- color: #000;
+ border-radius: 12px;
+ background: linear-gradient(135deg, #d4b08c 0%, #c9a27b 100%);
+ color: #0a0a0a;
font-family: inherit;
- font-weight: 700;
- font-size: 11px;
- letter-spacing: 0.1em;
- text-transform: uppercase;
+ font-weight: 600;
+ font-size: 13px;
+ letter-spacing: 0.01em;
cursor: pointer;
- transition: opacity 0.15s;
+ transition: all 0.25s ease;
+ box-shadow: 0 2px 12px rgba(212, 176, 140, 0.15);
}
button:hover {
- opacity: 0.8;
+ background: linear-gradient(135deg, #dfc09e 0%, #d4b08c 100%);
+ box-shadow: 0 4px 20px rgba(212, 176, 140, 0.25);
+ transform: translateY(-1px);
+}
+
+button:active {
+ transform: translateY(0);
+ box-shadow: 0 2px 8px rgba(212, 176, 140, 0.2);
}
button:disabled {
- opacity: 0.3;
+ opacity: 0.5;
cursor: not-allowed;
+ transform: none;
+ box-shadow: none;
}
-input[type="text"] {
- width: 100%;
- padding: 12px 16px;
- border: 1px solid rgba(255, 255, 255, 0.1);
- border-radius: 10px;
+button.ghost {
background: transparent;
- color: oklch(0.985 0 0);
- font-family: inherit;
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ color: rgba(255, 255, 255, 0.5);
+ padding: 8px 14px;
+ width: auto;
+ box-shadow: none;
+ font-weight: 500;
+}
+
+button.ghost:hover {
+ border-color: rgba(255, 255, 255, 0.12);
+ color: rgba(255, 255, 255, 0.8);
+ background: rgba(255, 255, 255, 0.03);
+ transform: none;
+}
+
+.link {
+ display: block;
+ text-align: center;
+ margin-top: 20px;
+ color: rgba(255, 255, 255, 0.4);
font-size: 12px;
+ text-decoration: none;
+ transition: color 0.2s ease;
}
-input[type="text"]:focus {
- outline: none;
- border-color: #ff6b00;
+.link:hover {
+ color: #d4b08c;
}
-.actions {
- display: flex;
- gap: 8px;
+.search {
+ position: relative;
+ margin-bottom: 20px;
+}
+
+.search svg {
+ position: absolute;
+ left: 14px;
+ top: 50%;
+ transform: translateY(-50%);
+ color: rgba(255, 255, 255, 0.2);
+ pointer-events: none;
+ transition: color 0.2s ease;
+}
+
+.search:focus-within svg {
+ color: rgba(212, 176, 140, 0.6);
+}
+
+.search input {
+ padding-left: 40px;
+ background: rgba(255, 255, 255, 0.03);
+ border-color: rgba(255, 255, 255, 0.04);
+}
+
+.search input:hover {
+ background: rgba(255, 255, 255, 0.04);
+ border-color: rgba(255, 255, 255, 0.06);
+}
+
+.search input:focus {
+ background: rgba(255, 255, 255, 0.04);
}
-.actions button {
+#items {
+ list-style: none;
flex: 1;
+ overflow-y: auto;
+ max-height: 260px;
+ margin: 0 -12px;
+ padding: 0 12px;
}
-.actions button:last-child {
+#items li {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ padding: 14px;
+ margin-bottom: 6px;
+ border-radius: 14px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ border: 1px solid transparent;
background: transparent;
- border: 1px solid rgba(255, 255, 255, 0.1);
- color: rgba(255, 255, 255, 0.4);
}
-.actions button:last-child:hover {
- border-color: rgba(255, 255, 255, 0.3);
- color: rgba(255, 255, 255, 0.6);
+#items li:hover {
+ background: rgba(255, 255, 255, 0.03);
+ border-color: rgba(255, 255, 255, 0.05);
}
-#recents {
- margin-top: 20px;
- padding-top: 16px;
- border-top: 1px solid rgba(255, 255, 255, 0.1);
- display: block !important;
+#items li:active {
+ background: rgba(255, 255, 255, 0.05);
+ transform: scale(0.99);
}
-#recents h3 {
- font-size: 10px;
- font-weight: 700;
+#items li .icon {
+ width: 40px;
+ height: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: linear-gradient(135deg, rgba(212, 176, 140, 0.12) 0%, rgba(212, 176, 140, 0.06) 100%);
+ border: 1px solid rgba(212, 176, 140, 0.1);
+ border-radius: 12px;
+ font-size: 14px;
+ font-weight: 600;
+ color: #d4b08c;
+ flex-shrink: 0;
+ font-family: "JetBrains Mono", ui-monospace, monospace;
+}
+
+#items li .info {
+ flex: 1;
+ min-width: 0;
+}
+
+#items li .title {
+ font-size: 13px;
+ font-weight: 500;
+ color: #ededed;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ margin-bottom: 3px;
+}
+
+#items li .subtitle {
+ font-size: 11px;
+ color: rgba(255, 255, 255, 0.35);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+#items li .star {
+ color: #d4b08c;
+ font-size: 12px;
+ opacity: 0.7;
+}
+
+#items .empty {
+ padding: 56px 24px;
+ text-align: center;
color: rgba(255, 255, 255, 0.4);
- text-transform: uppercase;
- letter-spacing: 0.1em;
- margin-bottom: 12px;
+ font-size: 13px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 16px;
+ cursor: default;
}
-#list {
- list-style: none;
+#items .empty .emptyicon {
+ width: 56px;
+ height: 56px;
+ background: linear-gradient(135deg, rgba(212, 176, 140, 0.08) 0%, rgba(212, 176, 140, 0.03) 100%);
+ border: 1px solid rgba(212, 176, 140, 0.08);
+ border-radius: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: rgba(212, 176, 140, 0.5);
}
-#list li {
+.footer {
display: flex;
align-items: center;
justify-content: space-between;
- padding: 10px 0;
- border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+ padding-top: 20px;
+ margin-top: auto;
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
}
-#list li:last-child {
- border-bottom: none;
+.email {
+ font-size: 11px;
+ color: rgba(255, 255, 255, 0.35);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ max-width: 160px;
}
-#list li.empty {
- color: rgba(255, 255, 255, 0.3);
+.back {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ margin-bottom: 24px;
font-size: 12px;
}
-#list .preview {
+.back svg {
+ transition: transform 0.2s ease;
+}
+
+.back:hover svg {
+ transform: translateX(-3px);
+}
+
+.detailheader {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ margin-bottom: 28px;
+ padding-bottom: 24px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+}
+
+.detailheader .icon {
+ width: 52px;
+ height: 52px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: linear-gradient(135deg, rgba(212, 176, 140, 0.15) 0%, rgba(212, 176, 140, 0.08) 100%);
+ border: 1px solid rgba(212, 176, 140, 0.15);
+ border-radius: 14px;
+ font-size: 18px;
+ font-weight: 600;
+ color: #d4b08c;
+ font-family: "JetBrains Mono", ui-monospace, monospace;
+}
+
+.detailheader h2 {
+ font-size: 16px;
+ font-weight: 600;
+ margin-bottom: 4px;
+ color: #ededed;
+}
+
+.detailheader .type {
+ font-size: 11px;
+ color: rgba(255, 255, 255, 0.4);
+ text-transform: lowercase;
+ font-weight: 500;
+}
+
+.fields {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.field {
+ background: #161616;
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ border-radius: 14px;
+ padding: 14px 16px;
+ transition: all 0.2s ease;
+}
+
+.field:hover {
+ border-color: rgba(255, 255, 255, 0.08);
+ background: #181818;
+}
+
+.field .label {
+ font-size: 10px;
+ color: rgba(255, 255, 255, 0.35);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ margin-bottom: 8px;
+ font-weight: 500;
+}
+
+.field .value {
+ font-size: 13px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+}
+
+.field .text {
flex: 1;
- font-size: 12px;
- color: rgba(255, 255, 255, 0.5);
+ min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- margin-right: 12px;
+ color: #ededed;
}
-#list button {
- width: auto;
- margin: 0;
- padding: 6px 12px;
- font-size: 10px;
- background: transparent;
- border: 1px solid rgba(255, 255, 255, 0.1);
+.field .text.password {
+ font-family: "JetBrains Mono", ui-monospace, monospace;
+ letter-spacing: 0.2em;
+ color: rgba(255, 255, 255, 0.5);
+ font-size: 12px;
+}
+
+.field .actions {
+ display: flex;
+ gap: 6px;
+}
+
+.field button {
+ width: 32px;
+ height: 32px;
+ padding: 0;
+ border-radius: 10px;
+ background: rgba(255, 255, 255, 0.04);
color: rgba(255, 255, 255, 0.4);
+ font-size: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px solid transparent;
+ box-shadow: none;
+}
+
+.field button:hover {
+ background: rgba(212, 176, 140, 0.12);
+ border-color: rgba(212, 176, 140, 0.15);
+ color: #d4b08c;
+ transform: none;
+ box-shadow: none;
+}
+
+.field button svg {
+ width: 14px;
+ height: 14px;
+}
+
+.field.autofill {
+ padding: 0;
+ background: transparent;
+ border: none;
+}
+
+.field.autofill:hover {
+ border-color: transparent;
+ background: transparent;
+}
+
+.autofillbtn {
+ width: 100%;
+ padding: 16px 20px;
+ border: none;
+ border-radius: 14px;
+ background: linear-gradient(135deg, #d4b08c 0%, #c9a27b 100%);
+ color: #0a0a0a;
+ font-family: inherit;
+ font-weight: 600;
+ font-size: 13px;
+ letter-spacing: 0.01em;
+ cursor: pointer;
+ transition: all 0.25s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 0 4px 16px rgba(212, 176, 140, 0.2);
+}
+
+.autofillbtn:hover {
+ background: linear-gradient(135deg, #dfc09e 0%, #d4b08c 100%);
+ box-shadow: 0 6px 24px rgba(212, 176, 140, 0.3);
+ transform: translateY(-1px);
}
-#list button:hover {
- border-color: #ff6b00;
- color: #ff6b00;
- opacity: 1;
+.autofillbtn:active {
+ transform: translateY(0);
+ box-shadow: 0 2px 12px rgba(212, 176, 140, 0.2);
}
diff --git a/apps/mobile-core/Cargo.lock b/apps/mobile-core/Cargo.lock
new file mode 100644
index 0000000..03ca03b
--- /dev/null
+++ b/apps/mobile-core/Cargo.lock
@@ -0,0 +1,1588 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
+[[package]]
+name = "aead"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
+dependencies = [
+ "crypto-common",
+ "generic-array",
+]
+
+[[package]]
+name = "anstream"
+version = "0.6.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
+dependencies = [
+ "anstyle",
+ "once_cell_polyfill",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
+
+[[package]]
+name = "argon2"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
+dependencies = [
+ "base64ct",
+ "blake2",
+ "cpufeatures",
+ "password-hash",
+]
+
+[[package]]
+name = "askama"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28"
+dependencies = [
+ "askama_derive",
+ "askama_escape",
+]
+
+[[package]]
+name = "askama_derive"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83"
+dependencies = [
+ "askama_parser",
+ "basic-toml",
+ "mime",
+ "mime_guess",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "syn",
+]
+
+[[package]]
+name = "askama_escape"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
+
+[[package]]
+name = "askama_parser"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "base64ct"
+version = "1.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
+
+[[package]]
+name = "basic-toml"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "blake2"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
+
+[[package]]
+name = "bytes"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
+
+[[package]]
+name = "camino"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "cargo-platform"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cargo_metadata"
+version = "0.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a"
+dependencies = [
+ "camino",
+ "cargo-platform",
+ "semver",
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "cc"
+version = "1.2.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583"
+dependencies = [
+ "find-msvc-tools",
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "chacha20"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+]
+
+[[package]]
+name = "chacha20poly1305"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
+dependencies = [
+ "aead",
+ "chacha20",
+ "cipher",
+ "poly1305",
+ "zeroize",
+]
+
+[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+ "zeroize",
+]
+
+[[package]]
+name = "clap"
+version = "4.5.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e34525d5bbbd55da2bb745d34b36121baac88d07619a9a09cfcf4a6c0832785"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59a20016a20a3da95bef50ec7238dbd09baeef4311dcdd38ec15aba69812fb61"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
+dependencies = [
+ "generic-array",
+ "rand_core",
+ "typenum",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db"
+
+[[package]]
+name = "flate2"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "fs-err"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasip2",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
+
+[[package]]
+name = "goblin"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b363a30c165f666402fe6a3024d3bec7ebc898f96a4a23bd1c99f8dbf3f4f47"
+dependencies = [
+ "log",
+ "plain",
+ "scroll",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "icu_collections"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
+dependencies = [
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
+
+[[package]]
+name = "icu_properties"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec"
+dependencies = [
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af"
+
+[[package]]
+name = "icu_provider"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "idna"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "inout"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
+
+[[package]]
+name = "itoa"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
+
+[[package]]
+name = "js-sys"
+version = "0.3.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.180"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
+
+[[package]]
+name = "litemap"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
+
+[[package]]
+name = "log"
+version = "0.4.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+
+[[package]]
+name = "memchr"
+version = "2.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "mime_guess"
+version = "2.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "noro-mobile-core"
+version = "0.1.0"
+dependencies = [
+ "argon2",
+ "base64",
+ "chacha20poly1305",
+ "rand",
+ "serde",
+ "serde_json",
+ "thiserror 2.0.18",
+ "uniffi",
+ "ureq",
+ "uuid",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "once_cell_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
+
+[[package]]
+name = "opaque-debug"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
+
+[[package]]
+name = "password-hash"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
+dependencies = [
+ "base64ct",
+ "rand_core",
+ "subtle",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+
+[[package]]
+name = "plain"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
+
+[[package]]
+name = "poly1305"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
+dependencies = [
+ "cpufeatures",
+ "opaque-debug",
+ "universal-hash",
+]
+
+[[package]]
+name = "potential_utf"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom 0.2.17",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom 0.2.17",
+ "libc",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.23.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b"
+dependencies = [
+ "log",
+ "once_cell",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
+dependencies = [
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.103.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "scroll"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6"
+dependencies = [
+ "scroll_derive",
+]
+
+[[package]]
+name = "scroll_derive"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
+dependencies = [
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.149"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
+dependencies = [
+ "itoa",
+ "memchr",
+ "serde",
+ "serde_core",
+ "zmij",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "simd-adler32"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
+
+[[package]]
+name = "siphasher"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "smawk"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "syn"
+version = "2.0.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.16.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
+dependencies = [
+ "smawk",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
+dependencies = [
+ "thiserror-impl 2.0.18",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "typenum"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
+
+[[package]]
+name = "unicase"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
+
+[[package]]
+name = "uniffi"
+version = "0.28.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cb08c58c7ed7033150132febe696bef553f891b1ede57424b40d87a89e3c170"
+dependencies = [
+ "anyhow",
+ "camino",
+ "cargo_metadata",
+ "clap",
+ "uniffi_bindgen",
+ "uniffi_core",
+ "uniffi_macros",
+]
+
+[[package]]
+name = "uniffi_bindgen"
+version = "0.28.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cade167af943e189a55020eda2c314681e223f1e42aca7c4e52614c2b627698f"
+dependencies = [
+ "anyhow",
+ "askama",
+ "camino",
+ "cargo_metadata",
+ "fs-err",
+ "glob",
+ "goblin",
+ "heck",
+ "once_cell",
+ "paste",
+ "serde",
+ "textwrap",
+ "toml",
+ "uniffi_meta",
+ "uniffi_udl",
+]
+
+[[package]]
+name = "uniffi_checksum_derive"
+version = "0.28.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "802d2051a700e3ec894c79f80d2705b69d85844dafbbe5d1a92776f8f48b563a"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "uniffi_core"
+version = "0.28.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc7687007d2546c454d8ae609b105daceb88175477dac280707ad6d95bcd6f1f"
+dependencies = [
+ "anyhow",
+ "bytes",
+ "log",
+ "once_cell",
+ "paste",
+ "static_assertions",
+]
+
+[[package]]
+name = "uniffi_macros"
+version = "0.28.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12c65a5b12ec544ef136693af8759fb9d11aefce740fb76916721e876639033b"
+dependencies = [
+ "bincode",
+ "camino",
+ "fs-err",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "syn",
+ "toml",
+ "uniffi_meta",
+]
+
+[[package]]
+name = "uniffi_meta"
+version = "0.28.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a74ed96c26882dac1ca9b93ca23c827e284bacbd7ec23c6f0b0372f747d59e4"
+dependencies = [
+ "anyhow",
+ "bytes",
+ "siphasher",
+ "uniffi_checksum_derive",
+]
+
+[[package]]
+name = "uniffi_testing"
+version = "0.28.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6f984f0781f892cc864a62c3a5c60361b1ccbd68e538e6c9fbced5d82268ac"
+dependencies = [
+ "anyhow",
+ "camino",
+ "cargo_metadata",
+ "fs-err",
+ "once_cell",
+]
+
+[[package]]
+name = "uniffi_udl"
+version = "0.28.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "037820a4cfc4422db1eaa82f291a3863c92c7d1789dc513489c36223f9b4cdfc"
+dependencies = [
+ "anyhow",
+ "textwrap",
+ "uniffi_meta",
+ "uniffi_testing",
+ "weedle2",
+]
+
+[[package]]
+name = "universal-hash"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
+dependencies = [
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "ureq"
+version = "2.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
+dependencies = [
+ "base64",
+ "flate2",
+ "log",
+ "once_cell",
+ "rustls",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "url",
+ "webpki-roots 0.26.11",
+]
+
+[[package]]
+name = "url"
+version = "2.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
+name = "uuid"
+version = "1.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f"
+dependencies = [
+ "getrandom 0.3.4",
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasip2"
+version = "1.0.2+wasi-0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.26.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
+dependencies = [
+ "webpki-roots 1.0.5",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "weedle2"
+version = "5.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "998d2c24ec099a87daf9467808859f9d82b61f1d9c9701251aea037f514eae0e"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "wit-bindgen"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+
+[[package]]
+name = "writeable"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
+
+[[package]]
+name = "yoke"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
+dependencies = [
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdea86ddd5568519879b8187e1cf04e24fce28f7fe046ceecbce472ff19a2572"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c15e1b46eff7c6c91195752e0eeed8ef040e391cdece7c25376957d5f15df22"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
+
+[[package]]
+name = "zerotrie"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zmij"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439"
diff --git a/apps/mobile-core/Cargo.toml b/apps/mobile-core/Cargo.toml
new file mode 100644
index 0000000..bfefaf6
--- /dev/null
+++ b/apps/mobile-core/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "noro-mobile-core"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "staticlib", "lib"]
+name = "noro_mobile_core"
+
+[dependencies]
+uniffi = { version = "0.28", features = ["cli"] }
+chacha20poly1305 = "0.10"
+argon2 = "0.5"
+rand = "0.8"
+thiserror = "2"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+base64 = "0.22"
+uuid = { version = "1", features = ["v4"] }
+ureq = { version = "2", features = ["json"] }
+
+[[bin]]
+name = "uniffi-bindgen"
+path = "src/bindgen.rs"
diff --git a/apps/mobile-core/bindings/NoroCore.swift b/apps/mobile-core/bindings/NoroCore.swift
new file mode 100644
index 0000000..018cead
--- /dev/null
+++ b/apps/mobile-core/bindings/NoroCore.swift
@@ -0,0 +1,1657 @@
+// This file was autogenerated by some hot garbage in the `uniffi` crate.
+// Trust me, you don't want to mess with it!
+
+// swiftlint:disable all
+import Foundation
+
+// Depending on the consumer's build setup, the low-level FFI code
+// might be in a separate module, or it might be compiled inline into
+// this module. This is a bit of light hackery to work with both.
+#if canImport(NoroCoreFFI)
+import NoroCoreFFI
+#endif
+
+fileprivate extension RustBuffer {
+ // Allocate a new buffer, copying the contents of a `UInt8` array.
+ init(bytes: [UInt8]) {
+ let rbuf = bytes.withUnsafeBufferPointer { ptr in
+ RustBuffer.from(ptr)
+ }
+ self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data)
+ }
+
+ static func empty() -> RustBuffer {
+ RustBuffer(capacity: 0, len:0, data: nil)
+ }
+
+ static func from(_ ptr: UnsafeBufferPointer) -> RustBuffer {
+ try! rustCall { ffi_noro_mobile_core_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) }
+ }
+
+ // Frees the buffer in place.
+ // The buffer must not be used after this is called.
+ func deallocate() {
+ try! rustCall { ffi_noro_mobile_core_rustbuffer_free(self, $0) }
+ }
+}
+
+fileprivate extension ForeignBytes {
+ init(bufferPointer: UnsafeBufferPointer) {
+ self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress)
+ }
+}
+
+// For every type used in the interface, we provide helper methods for conveniently
+// lifting and lowering that type from C-compatible data, and for reading and writing
+// values of that type in a buffer.
+
+// Helper classes/extensions that don't change.
+// Someday, this will be in a library of its own.
+
+fileprivate extension Data {
+ init(rustBuffer: RustBuffer) {
+ self.init(
+ bytesNoCopy: rustBuffer.data!,
+ count: Int(rustBuffer.len),
+ deallocator: .none
+ )
+ }
+}
+
+// Define reader functionality. Normally this would be defined in a class or
+// struct, but we use standalone functions instead in order to make external
+// types work.
+//
+// With external types, one swift source file needs to be able to call the read
+// method on another source file's FfiConverter, but then what visibility
+// should Reader have?
+// - If Reader is fileprivate, then this means the read() must also
+// be fileprivate, which doesn't work with external types.
+// - If Reader is internal/public, we'll get compile errors since both source
+// files will try define the same type.
+//
+// Instead, the read() method and these helper functions input a tuple of data
+
+fileprivate func createReader(data: Data) -> (data: Data, offset: Data.Index) {
+ (data: data, offset: 0)
+}
+
+// Reads an integer at the current offset, in big-endian order, and advances
+// the offset on success. Throws if reading the integer would move the
+// offset past the end of the buffer.
+fileprivate func readInt(_ reader: inout (data: Data, offset: Data.Index)) throws -> T {
+ let range = reader.offset...size
+ guard reader.data.count >= range.upperBound else {
+ throw UniffiInternalError.bufferOverflow
+ }
+ if T.self == UInt8.self {
+ let value = reader.data[reader.offset]
+ reader.offset += 1
+ return value as! T
+ }
+ var value: T = 0
+ let _ = withUnsafeMutableBytes(of: &value, { reader.data.copyBytes(to: $0, from: range)})
+ reader.offset = range.upperBound
+ return value.bigEndian
+}
+
+// Reads an arbitrary number of bytes, to be used to read
+// raw bytes, this is useful when lifting strings
+fileprivate func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: Int) throws -> Array {
+ let range = reader.offset..<(reader.offset+count)
+ guard reader.data.count >= range.upperBound else {
+ throw UniffiInternalError.bufferOverflow
+ }
+ var value = [UInt8](repeating: 0, count: count)
+ value.withUnsafeMutableBufferPointer({ buffer in
+ reader.data.copyBytes(to: buffer, from: range)
+ })
+ reader.offset = range.upperBound
+ return value
+}
+
+// Reads a float at the current offset.
+fileprivate func readFloat(_ reader: inout (data: Data, offset: Data.Index)) throws -> Float {
+ return Float(bitPattern: try readInt(&reader))
+}
+
+// Reads a float at the current offset.
+fileprivate func readDouble(_ reader: inout (data: Data, offset: Data.Index)) throws -> Double {
+ return Double(bitPattern: try readInt(&reader))
+}
+
+// Indicates if the offset has reached the end of the buffer.
+fileprivate func hasRemaining(_ reader: (data: Data, offset: Data.Index)) -> Bool {
+ return reader.offset < reader.data.count
+}
+
+// Define writer functionality. Normally this would be defined in a class or
+// struct, but we use standalone functions instead in order to make external
+// types work. See the above discussion on Readers for details.
+
+fileprivate func createWriter() -> [UInt8] {
+ return []
+}
+
+fileprivate func writeBytes(_ writer: inout [UInt8], _ byteArr: S) where S: Sequence, S.Element == UInt8 {
+ writer.append(contentsOf: byteArr)
+}
+
+// Writes an integer in big-endian order.
+//
+// Warning: make sure what you are trying to write
+// is in the correct type!
+fileprivate func writeInt(_ writer: inout [UInt8], _ value: T) {
+ var value = value.bigEndian
+ withUnsafeBytes(of: &value) { writer.append(contentsOf: $0) }
+}
+
+fileprivate func writeFloat(_ writer: inout [UInt8], _ value: Float) {
+ writeInt(&writer, value.bitPattern)
+}
+
+fileprivate func writeDouble(_ writer: inout [UInt8], _ value: Double) {
+ writeInt(&writer, value.bitPattern)
+}
+
+// Protocol for types that transfer other types across the FFI. This is
+// analogous to the Rust trait of the same name.
+fileprivate protocol FfiConverter {
+ associatedtype FfiType
+ associatedtype SwiftType
+
+ static func lift(_ value: FfiType) throws -> SwiftType
+ static func lower(_ value: SwiftType) -> FfiType
+ static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType
+ static func write(_ value: SwiftType, into buf: inout [UInt8])
+}
+
+// Types conforming to `Primitive` pass themselves directly over the FFI.
+fileprivate protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType { }
+
+extension FfiConverterPrimitive {
+#if swift(>=5.8)
+ @_documentation(visibility: private)
+#endif
+ public static func lift(_ value: FfiType) throws -> SwiftType {
+ return value
+ }
+
+#if swift(>=5.8)
+ @_documentation(visibility: private)
+#endif
+ public static func lower(_ value: SwiftType) -> FfiType {
+ return value
+ }
+}
+
+// Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`.
+// Used for complex types where it's hard to write a custom lift/lower.
+fileprivate protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {}
+
+extension FfiConverterRustBuffer {
+#if swift(>=5.8)
+ @_documentation(visibility: private)
+#endif
+ public static func lift(_ buf: RustBuffer) throws -> SwiftType {
+ var reader = createReader(data: Data(rustBuffer: buf))
+ let value = try read(from: &reader)
+ if hasRemaining(reader) {
+ throw UniffiInternalError.incompleteData
+ }
+ buf.deallocate()
+ return value
+ }
+
+#if swift(>=5.8)
+ @_documentation(visibility: private)
+#endif
+ public static func lower(_ value: SwiftType) -> RustBuffer {
+ var writer = createWriter()
+ write(value, into: &writer)
+ return RustBuffer(bytes: writer)
+ }
+}
+// An error type for FFI errors. These errors occur at the UniFFI level, not
+// the library level.
+fileprivate enum UniffiInternalError: LocalizedError {
+ case bufferOverflow
+ case incompleteData
+ case unexpectedOptionalTag
+ case unexpectedEnumCase
+ case unexpectedNullPointer
+ case unexpectedRustCallStatusCode
+ case unexpectedRustCallError
+ case unexpectedStaleHandle
+ case rustPanic(_ message: String)
+
+ public var errorDescription: String? {
+ switch self {
+ case .bufferOverflow: return "Reading the requested value would read past the end of the buffer"
+ case .incompleteData: return "The buffer still has data after lifting its containing value"
+ case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1"
+ case .unexpectedEnumCase: return "Raw enum value doesn't match any cases"
+ case .unexpectedNullPointer: return "Raw pointer value was null"
+ case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code"
+ case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified"
+ case .unexpectedStaleHandle: return "The object in the handle map has been dropped already"
+ case let .rustPanic(message): return message
+ }
+ }
+}
+
+fileprivate extension NSLock {
+ func withLock(f: () throws -> T) rethrows -> T {
+ self.lock()
+ defer { self.unlock() }
+ return try f()
+ }
+}
+
+fileprivate let CALL_SUCCESS: Int8 = 0
+fileprivate let CALL_ERROR: Int8 = 1
+fileprivate let CALL_UNEXPECTED_ERROR: Int8 = 2
+fileprivate let CALL_CANCELLED: Int8 = 3
+
+fileprivate extension RustCallStatus {
+ init() {
+ self.init(
+ code: CALL_SUCCESS,
+ errorBuf: RustBuffer.init(
+ capacity: 0,
+ len: 0,
+ data: nil
+ )
+ )
+ }
+}
+
+private func rustCall(_ callback: (UnsafeMutablePointer) -> T) throws -> T {
+ let neverThrow: ((RustBuffer) throws -> Never)? = nil
+ return try makeRustCall(callback, errorHandler: neverThrow)
+}
+
+private func rustCallWithError(
+ _ errorHandler: @escaping (RustBuffer) throws -> E,
+ _ callback: (UnsafeMutablePointer) -> T) throws -> T {
+ try makeRustCall(callback, errorHandler: errorHandler)
+}
+
+private func makeRustCall(
+ _ callback: (UnsafeMutablePointer) -> T,
+ errorHandler: ((RustBuffer) throws -> E)?
+) throws -> T {
+ uniffiEnsureInitialized()
+ var callStatus = RustCallStatus.init()
+ let returnedVal = callback(&callStatus)
+ try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler)
+ return returnedVal
+}
+
+private func uniffiCheckCallStatus(
+ callStatus: RustCallStatus,
+ errorHandler: ((RustBuffer) throws -> E)?
+) throws {
+ switch callStatus.code {
+ case CALL_SUCCESS:
+ return
+
+ case CALL_ERROR:
+ if let errorHandler = errorHandler {
+ throw try errorHandler(callStatus.errorBuf)
+ } else {
+ callStatus.errorBuf.deallocate()
+ throw UniffiInternalError.unexpectedRustCallError
+ }
+
+ case CALL_UNEXPECTED_ERROR:
+ // When the rust code sees a panic, it tries to construct a RustBuffer
+ // with the message. But if that code panics, then it just sends back
+ // an empty buffer.
+ if callStatus.errorBuf.len > 0 {
+ throw UniffiInternalError.rustPanic(try FfiConverterString.lift(callStatus.errorBuf))
+ } else {
+ callStatus.errorBuf.deallocate()
+ throw UniffiInternalError.rustPanic("Rust panic")
+ }
+
+ case CALL_CANCELLED:
+ fatalError("Cancellation not supported yet")
+
+ default:
+ throw UniffiInternalError.unexpectedRustCallStatusCode
+ }
+}
+
+private func uniffiTraitInterfaceCall(
+ callStatus: UnsafeMutablePointer,
+ makeCall: () throws -> T,
+ writeReturn: (T) -> ()
+) {
+ do {
+ try writeReturn(makeCall())
+ } catch let error {
+ callStatus.pointee.code = CALL_UNEXPECTED_ERROR
+ callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error))
+ }
+}
+
+private func uniffiTraitInterfaceCallWithError(
+ callStatus: UnsafeMutablePointer,
+ makeCall: () throws -> T,
+ writeReturn: (T) -> (),
+ lowerError: (E) -> RustBuffer
+) {
+ do {
+ try writeReturn(makeCall())
+ } catch let error as E {
+ callStatus.pointee.code = CALL_ERROR
+ callStatus.pointee.errorBuf = lowerError(error)
+ } catch {
+ callStatus.pointee.code = CALL_UNEXPECTED_ERROR
+ callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error))
+ }
+}
+fileprivate class UniffiHandleMap {
+ private var map: [UInt64: T] = [:]
+ private let lock = NSLock()
+ private var currentHandle: UInt64 = 1
+
+ func insert(obj: T) -> UInt64 {
+ lock.withLock {
+ let handle = currentHandle
+ currentHandle += 1
+ map[handle] = obj
+ return handle
+ }
+ }
+
+ func get(handle: UInt64) throws -> T {
+ try lock.withLock {
+ guard let obj = map[handle] else {
+ throw UniffiInternalError.unexpectedStaleHandle
+ }
+ return obj
+ }
+ }
+
+ @discardableResult
+ func remove(handle: UInt64) throws -> T {
+ try lock.withLock {
+ guard let obj = map.removeValue(forKey: handle) else {
+ throw UniffiInternalError.unexpectedStaleHandle
+ }
+ return obj
+ }
+ }
+
+ var count: Int {
+ get {
+ map.count
+ }
+ }
+}
+
+
+// Public interface members begin here.
+
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+fileprivate struct FfiConverterInt32: FfiConverterPrimitive {
+ typealias FfiType = Int32
+ typealias SwiftType = Int32
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Int32 {
+ return try lift(readInt(&buf))
+ }
+
+ public static func write(_ value: Int32, into buf: inout [UInt8]) {
+ writeInt(&buf, lower(value))
+ }
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+fileprivate struct FfiConverterUInt64: FfiConverterPrimitive {
+ typealias FfiType = UInt64
+ typealias SwiftType = UInt64
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt64 {
+ return try lift(readInt(&buf))
+ }
+
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
+ writeInt(&buf, lower(value))
+ }
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+fileprivate struct FfiConverterBool : FfiConverter {
+ typealias FfiType = Int8
+ typealias SwiftType = Bool
+
+ public static func lift(_ value: Int8) throws -> Bool {
+ return value != 0
+ }
+
+ public static func lower(_ value: Bool) -> Int8 {
+ return value ? 1 : 0
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Bool {
+ return try lift(readInt(&buf))
+ }
+
+ public static func write(_ value: Bool, into buf: inout [UInt8]) {
+ writeInt(&buf, lower(value))
+ }
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+fileprivate struct FfiConverterString: FfiConverter {
+ typealias SwiftType = String
+ typealias FfiType = RustBuffer
+
+ public static func lift(_ value: RustBuffer) throws -> String {
+ defer {
+ value.deallocate()
+ }
+ if value.data == nil {
+ return String()
+ }
+ let bytes = UnsafeBufferPointer(start: value.data!, count: Int(value.len))
+ return String(bytes: bytes, encoding: String.Encoding.utf8)!
+ }
+
+ public static func lower(_ value: String) -> RustBuffer {
+ return value.utf8CString.withUnsafeBufferPointer { ptr in
+ // The swift string gives us int8_t, we want uint8_t.
+ ptr.withMemoryRebound(to: UInt8.self) { ptr in
+ // The swift string gives us a trailing null byte, we don't want it.
+ let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1))
+ return RustBuffer.from(buf)
+ }
+ }
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> String {
+ let len: Int32 = try readInt(&buf)
+ return String(bytes: try readBytes(&buf, count: Int(len)), encoding: String.Encoding.utf8)!
+ }
+
+ public static func write(_ value: String, into buf: inout [UInt8]) {
+ let len = Int32(value.utf8.count)
+ writeInt(&buf, len)
+ writeBytes(&buf, value.utf8)
+ }
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+fileprivate struct FfiConverterData: FfiConverterRustBuffer {
+ typealias SwiftType = Data
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Data {
+ let len: Int32 = try readInt(&buf)
+ return Data(try readBytes(&buf, count: Int(len)))
+ }
+
+ public static func write(_ value: Data, into buf: inout [UInt8]) {
+ let len = Int32(value.count)
+ writeInt(&buf, len)
+ writeBytes(&buf, value)
+ }
+}
+
+
+
+
+public protocol SyncClientProtocol : AnyObject {
+
+ func createItem(itemType: String, title: String, data: Data, tags: [String], favorite: Bool) throws -> VaultItem
+
+ func deleteItem(id: String) throws
+
+ func fetchItems() throws -> [VaultItem]
+
+ func login(email: String, password: String) throws -> String
+
+ func setToken(token: String)
+
+ func updateItem(id: String, title: String?, data: Data?, tags: [String]?, favorite: Bool?) throws -> VaultItem
+
+}
+
+open class SyncClient:
+ SyncClientProtocol {
+ fileprivate let pointer: UnsafeMutableRawPointer!
+
+ /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly.
+#if swift(>=5.8)
+ @_documentation(visibility: private)
+#endif
+ public struct NoPointer {
+ public init() {}
+ }
+
+ // TODO: We'd like this to be `private` but for Swifty reasons,
+ // we can't implement `FfiConverter` without making this `required` and we can't
+ // make it `required` without making it `public`.
+ required public init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) {
+ self.pointer = pointer
+ }
+
+ // This constructor can be used to instantiate a fake object.
+ // - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject].
+ //
+ // - Warning:
+ // Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing [Pointer] the FFI lower functions will crash.
+#if swift(>=5.8)
+ @_documentation(visibility: private)
+#endif
+ public init(noPointer: NoPointer) {
+ self.pointer = nil
+ }
+
+#if swift(>=5.8)
+ @_documentation(visibility: private)
+#endif
+ public func uniffiClonePointer() -> UnsafeMutableRawPointer {
+ return try! rustCall { uniffi_noro_mobile_core_fn_clone_syncclient(self.pointer, $0) }
+ }
+public convenience init(baseUrl: String) {
+ let pointer =
+ try! rustCall() {
+ uniffi_noro_mobile_core_fn_constructor_syncclient_new(
+ FfiConverterString.lower(baseUrl),$0
+ )
+}
+ self.init(unsafeFromRawPointer: pointer)
+}
+
+ deinit {
+ guard let pointer = pointer else {
+ return
+ }
+
+ try! rustCall { uniffi_noro_mobile_core_fn_free_syncclient(pointer, $0) }
+ }
+
+
+
+
+open func createItem(itemType: String, title: String, data: Data, tags: [String], favorite: Bool)throws -> VaultItem {
+ return try FfiConverterTypeVaultItem.lift(try rustCallWithError(FfiConverterTypeSyncError.lift) {
+ uniffi_noro_mobile_core_fn_method_syncclient_create_item(self.uniffiClonePointer(),
+ FfiConverterString.lower(itemType),
+ FfiConverterString.lower(title),
+ FfiConverterData.lower(data),
+ FfiConverterSequenceString.lower(tags),
+ FfiConverterBool.lower(favorite),$0
+ )
+})
+}
+
+open func deleteItem(id: String)throws {try rustCallWithError(FfiConverterTypeSyncError.lift) {
+ uniffi_noro_mobile_core_fn_method_syncclient_delete_item(self.uniffiClonePointer(),
+ FfiConverterString.lower(id),$0
+ )
+}
+}
+
+open func fetchItems()throws -> [VaultItem] {
+ return try FfiConverterSequenceTypeVaultItem.lift(try rustCallWithError(FfiConverterTypeSyncError.lift) {
+ uniffi_noro_mobile_core_fn_method_syncclient_fetch_items(self.uniffiClonePointer(),$0
+ )
+})
+}
+
+open func login(email: String, password: String)throws -> String {
+ return try FfiConverterString.lift(try rustCallWithError(FfiConverterTypeSyncError.lift) {
+ uniffi_noro_mobile_core_fn_method_syncclient_login(self.uniffiClonePointer(),
+ FfiConverterString.lower(email),
+ FfiConverterString.lower(password),$0
+ )
+})
+}
+
+open func setToken(token: String) {try! rustCall() {
+ uniffi_noro_mobile_core_fn_method_syncclient_set_token(self.uniffiClonePointer(),
+ FfiConverterString.lower(token),$0
+ )
+}
+}
+
+open func updateItem(id: String, title: String?, data: Data?, tags: [String]?, favorite: Bool?)throws -> VaultItem {
+ return try FfiConverterTypeVaultItem.lift(try rustCallWithError(FfiConverterTypeSyncError.lift) {
+ uniffi_noro_mobile_core_fn_method_syncclient_update_item(self.uniffiClonePointer(),
+ FfiConverterString.lower(id),
+ FfiConverterOptionString.lower(title),
+ FfiConverterOptionData.lower(data),
+ FfiConverterOptionSequenceString.lower(tags),
+ FfiConverterOptionBool.lower(favorite),$0
+ )
+})
+}
+
+
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+public struct FfiConverterTypeSyncClient: FfiConverter {
+
+ typealias FfiType = UnsafeMutableRawPointer
+ typealias SwiftType = SyncClient
+
+ public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> SyncClient {
+ return SyncClient(unsafeFromRawPointer: pointer)
+ }
+
+ public static func lower(_ value: SyncClient) -> UnsafeMutableRawPointer {
+ return value.uniffiClonePointer()
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SyncClient {
+ let v: UInt64 = try readInt(&buf)
+ // The Rust code won't compile if a pointer won't fit in a UInt64.
+ // We have to go via `UInt` because that's the thing that's the size of a pointer.
+ let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v))
+ if (ptr == nil) {
+ throw UniffiInternalError.unexpectedNullPointer
+ }
+ return try lift(ptr!)
+ }
+
+ public static func write(_ value: SyncClient, into buf: inout [UInt8]) {
+ // This fiddling is because `Int` is the thing that's the same size as a pointer.
+ // The Rust code won't compile if a pointer won't fit in a `UInt64`.
+ writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value)))))
+ }
+}
+
+
+
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+public func FfiConverterTypeSyncClient_lift(_ pointer: UnsafeMutableRawPointer) throws -> SyncClient {
+ return try FfiConverterTypeSyncClient.lift(pointer)
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+public func FfiConverterTypeSyncClient_lower(_ value: SyncClient) -> UnsafeMutableRawPointer {
+ return FfiConverterTypeSyncClient.lower(value)
+}
+
+
+
+
+public protocol VaultProtocol : AnyObject {
+
+ func createItem(itemType: String, title: String, data: Data, tags: [String], favorite: Bool) throws -> VaultItem
+
+ func deleteItem(id: String) throws
+
+ func getItem(id: String) throws -> VaultItem?
+
+ func listItems() -> [VaultItem]
+
+ func load(encrypted: Data, key: Data) throws
+
+ func save(key: Data) throws -> Data
+
+ func searchItems(query: String) -> [VaultItem]
+
+ func updateItem(id: String, title: String?, data: Data?, tags: [String]?, favorite: Bool?) throws -> VaultItem
+
+}
+
+open class Vault:
+ VaultProtocol {
+ fileprivate let pointer: UnsafeMutableRawPointer!
+
+ /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly.
+#if swift(>=5.8)
+ @_documentation(visibility: private)
+#endif
+ public struct NoPointer {
+ public init() {}
+ }
+
+ // TODO: We'd like this to be `private` but for Swifty reasons,
+ // we can't implement `FfiConverter` without making this `required` and we can't
+ // make it `required` without making it `public`.
+ required public init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) {
+ self.pointer = pointer
+ }
+
+ // This constructor can be used to instantiate a fake object.
+ // - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject].
+ //
+ // - Warning:
+ // Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing [Pointer] the FFI lower functions will crash.
+#if swift(>=5.8)
+ @_documentation(visibility: private)
+#endif
+ public init(noPointer: NoPointer) {
+ self.pointer = nil
+ }
+
+#if swift(>=5.8)
+ @_documentation(visibility: private)
+#endif
+ public func uniffiClonePointer() -> UnsafeMutableRawPointer {
+ return try! rustCall { uniffi_noro_mobile_core_fn_clone_vault(self.pointer, $0) }
+ }
+public convenience init() {
+ let pointer =
+ try! rustCall() {
+ uniffi_noro_mobile_core_fn_constructor_vault_new($0
+ )
+}
+ self.init(unsafeFromRawPointer: pointer)
+}
+
+ deinit {
+ guard let pointer = pointer else {
+ return
+ }
+
+ try! rustCall { uniffi_noro_mobile_core_fn_free_vault(pointer, $0) }
+ }
+
+
+
+
+open func createItem(itemType: String, title: String, data: Data, tags: [String], favorite: Bool)throws -> VaultItem {
+ return try FfiConverterTypeVaultItem.lift(try rustCallWithError(FfiConverterTypeVaultError.lift) {
+ uniffi_noro_mobile_core_fn_method_vault_create_item(self.uniffiClonePointer(),
+ FfiConverterString.lower(itemType),
+ FfiConverterString.lower(title),
+ FfiConverterData.lower(data),
+ FfiConverterSequenceString.lower(tags),
+ FfiConverterBool.lower(favorite),$0
+ )
+})
+}
+
+open func deleteItem(id: String)throws {try rustCallWithError(FfiConverterTypeVaultError.lift) {
+ uniffi_noro_mobile_core_fn_method_vault_delete_item(self.uniffiClonePointer(),
+ FfiConverterString.lower(id),$0
+ )
+}
+}
+
+open func getItem(id: String)throws -> VaultItem? {
+ return try FfiConverterOptionTypeVaultItem.lift(try rustCallWithError(FfiConverterTypeVaultError.lift) {
+ uniffi_noro_mobile_core_fn_method_vault_get_item(self.uniffiClonePointer(),
+ FfiConverterString.lower(id),$0
+ )
+})
+}
+
+open func listItems() -> [VaultItem] {
+ return try! FfiConverterSequenceTypeVaultItem.lift(try! rustCall() {
+ uniffi_noro_mobile_core_fn_method_vault_list_items(self.uniffiClonePointer(),$0
+ )
+})
+}
+
+open func load(encrypted: Data, key: Data)throws {try rustCallWithError(FfiConverterTypeVaultError.lift) {
+ uniffi_noro_mobile_core_fn_method_vault_load(self.uniffiClonePointer(),
+ FfiConverterData.lower(encrypted),
+ FfiConverterData.lower(key),$0
+ )
+}
+}
+
+open func save(key: Data)throws -> Data {
+ return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeVaultError.lift) {
+ uniffi_noro_mobile_core_fn_method_vault_save(self.uniffiClonePointer(),
+ FfiConverterData.lower(key),$0
+ )
+})
+}
+
+open func searchItems(query: String) -> [VaultItem] {
+ return try! FfiConverterSequenceTypeVaultItem.lift(try! rustCall() {
+ uniffi_noro_mobile_core_fn_method_vault_search_items(self.uniffiClonePointer(),
+ FfiConverterString.lower(query),$0
+ )
+})
+}
+
+open func updateItem(id: String, title: String?, data: Data?, tags: [String]?, favorite: Bool?)throws -> VaultItem {
+ return try FfiConverterTypeVaultItem.lift(try rustCallWithError(FfiConverterTypeVaultError.lift) {
+ uniffi_noro_mobile_core_fn_method_vault_update_item(self.uniffiClonePointer(),
+ FfiConverterString.lower(id),
+ FfiConverterOptionString.lower(title),
+ FfiConverterOptionData.lower(data),
+ FfiConverterOptionSequenceString.lower(tags),
+ FfiConverterOptionBool.lower(favorite),$0
+ )
+})
+}
+
+
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+public struct FfiConverterTypeVault: FfiConverter {
+
+ typealias FfiType = UnsafeMutableRawPointer
+ typealias SwiftType = Vault
+
+ public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> Vault {
+ return Vault(unsafeFromRawPointer: pointer)
+ }
+
+ public static func lower(_ value: Vault) -> UnsafeMutableRawPointer {
+ return value.uniffiClonePointer()
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Vault {
+ let v: UInt64 = try readInt(&buf)
+ // The Rust code won't compile if a pointer won't fit in a UInt64.
+ // We have to go via `UInt` because that's the thing that's the size of a pointer.
+ let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v))
+ if (ptr == nil) {
+ throw UniffiInternalError.unexpectedNullPointer
+ }
+ return try lift(ptr!)
+ }
+
+ public static func write(_ value: Vault, into buf: inout [UInt8]) {
+ // This fiddling is because `Int` is the thing that's the same size as a pointer.
+ // The Rust code won't compile if a pointer won't fit in a `UInt64`.
+ writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value)))))
+ }
+}
+
+
+
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+public func FfiConverterTypeVault_lift(_ pointer: UnsafeMutableRawPointer) throws -> Vault {
+ return try FfiConverterTypeVault.lift(pointer)
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+public func FfiConverterTypeVault_lower(_ value: Vault) -> UnsafeMutableRawPointer {
+ return FfiConverterTypeVault.lower(value)
+}
+
+
+public struct VaultData {
+ public var items: [VaultItem]
+ public var updated: UInt64
+
+ // Default memberwise initializers are never public by default, so we
+ // declare one manually.
+ public init(items: [VaultItem], updated: UInt64) {
+ self.items = items
+ self.updated = updated
+ }
+}
+
+
+
+extension VaultData: Equatable, Hashable {
+ public static func ==(lhs: VaultData, rhs: VaultData) -> Bool {
+ if lhs.items != rhs.items {
+ return false
+ }
+ if lhs.updated != rhs.updated {
+ return false
+ }
+ return true
+ }
+
+ public func hash(into hasher: inout Hasher) {
+ hasher.combine(items)
+ hasher.combine(updated)
+ }
+}
+
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+public struct FfiConverterTypeVaultData: FfiConverterRustBuffer {
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> VaultData {
+ return
+ try VaultData(
+ items: FfiConverterSequenceTypeVaultItem.read(from: &buf),
+ updated: FfiConverterUInt64.read(from: &buf)
+ )
+ }
+
+ public static func write(_ value: VaultData, into buf: inout [UInt8]) {
+ FfiConverterSequenceTypeVaultItem.write(value.items, into: &buf)
+ FfiConverterUInt64.write(value.updated, into: &buf)
+ }
+}
+
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+public func FfiConverterTypeVaultData_lift(_ buf: RustBuffer) throws -> VaultData {
+ return try FfiConverterTypeVaultData.lift(buf)
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+public func FfiConverterTypeVaultData_lower(_ value: VaultData) -> RustBuffer {
+ return FfiConverterTypeVaultData.lower(value)
+}
+
+
+public struct VaultItem {
+ public var id: String
+ public var itemType: String
+ public var title: String
+ public var data: Data
+ public var revision: Int32
+ public var favorite: Bool
+ public var deleted: Bool
+ public var tags: [String]
+ public var created: UInt64
+ public var updated: UInt64
+
+ // Default memberwise initializers are never public by default, so we
+ // declare one manually.
+ public init(id: String, itemType: String, title: String, data: Data, revision: Int32, favorite: Bool, deleted: Bool, tags: [String], created: UInt64, updated: UInt64) {
+ self.id = id
+ self.itemType = itemType
+ self.title = title
+ self.data = data
+ self.revision = revision
+ self.favorite = favorite
+ self.deleted = deleted
+ self.tags = tags
+ self.created = created
+ self.updated = updated
+ }
+}
+
+
+
+extension VaultItem: Equatable, Hashable {
+ public static func ==(lhs: VaultItem, rhs: VaultItem) -> Bool {
+ if lhs.id != rhs.id {
+ return false
+ }
+ if lhs.itemType != rhs.itemType {
+ return false
+ }
+ if lhs.title != rhs.title {
+ return false
+ }
+ if lhs.data != rhs.data {
+ return false
+ }
+ if lhs.revision != rhs.revision {
+ return false
+ }
+ if lhs.favorite != rhs.favorite {
+ return false
+ }
+ if lhs.deleted != rhs.deleted {
+ return false
+ }
+ if lhs.tags != rhs.tags {
+ return false
+ }
+ if lhs.created != rhs.created {
+ return false
+ }
+ if lhs.updated != rhs.updated {
+ return false
+ }
+ return true
+ }
+
+ public func hash(into hasher: inout Hasher) {
+ hasher.combine(id)
+ hasher.combine(itemType)
+ hasher.combine(title)
+ hasher.combine(data)
+ hasher.combine(revision)
+ hasher.combine(favorite)
+ hasher.combine(deleted)
+ hasher.combine(tags)
+ hasher.combine(created)
+ hasher.combine(updated)
+ }
+}
+
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+public struct FfiConverterTypeVaultItem: FfiConverterRustBuffer {
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> VaultItem {
+ return
+ try VaultItem(
+ id: FfiConverterString.read(from: &buf),
+ itemType: FfiConverterString.read(from: &buf),
+ title: FfiConverterString.read(from: &buf),
+ data: FfiConverterData.read(from: &buf),
+ revision: FfiConverterInt32.read(from: &buf),
+ favorite: FfiConverterBool.read(from: &buf),
+ deleted: FfiConverterBool.read(from: &buf),
+ tags: FfiConverterSequenceString.read(from: &buf),
+ created: FfiConverterUInt64.read(from: &buf),
+ updated: FfiConverterUInt64.read(from: &buf)
+ )
+ }
+
+ public static func write(_ value: VaultItem, into buf: inout [UInt8]) {
+ FfiConverterString.write(value.id, into: &buf)
+ FfiConverterString.write(value.itemType, into: &buf)
+ FfiConverterString.write(value.title, into: &buf)
+ FfiConverterData.write(value.data, into: &buf)
+ FfiConverterInt32.write(value.revision, into: &buf)
+ FfiConverterBool.write(value.favorite, into: &buf)
+ FfiConverterBool.write(value.deleted, into: &buf)
+ FfiConverterSequenceString.write(value.tags, into: &buf)
+ FfiConverterUInt64.write(value.created, into: &buf)
+ FfiConverterUInt64.write(value.updated, into: &buf)
+ }
+}
+
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+public func FfiConverterTypeVaultItem_lift(_ buf: RustBuffer) throws -> VaultItem {
+ return try FfiConverterTypeVaultItem.lift(buf)
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+public func FfiConverterTypeVaultItem_lower(_ value: VaultItem) -> RustBuffer {
+ return FfiConverterTypeVaultItem.lower(value)
+}
+
+
+public enum CryptoError {
+
+
+
+ case Argon2
+ case Encryption
+ case Decryption
+ case InvalidSecretKey
+ case InvalidKeyLength
+}
+
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+public struct FfiConverterTypeCryptoError: FfiConverterRustBuffer {
+ typealias SwiftType = CryptoError
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CryptoError {
+ let variant: Int32 = try readInt(&buf)
+ switch variant {
+
+
+
+
+ case 1: return .Argon2
+ case 2: return .Encryption
+ case 3: return .Decryption
+ case 4: return .InvalidSecretKey
+ case 5: return .InvalidKeyLength
+
+ default: throw UniffiInternalError.unexpectedEnumCase
+ }
+ }
+
+ public static func write(_ value: CryptoError, into buf: inout [UInt8]) {
+ switch value {
+
+
+
+
+
+ case .Argon2:
+ writeInt(&buf, Int32(1))
+
+
+ case .Encryption:
+ writeInt(&buf, Int32(2))
+
+
+ case .Decryption:
+ writeInt(&buf, Int32(3))
+
+
+ case .InvalidSecretKey:
+ writeInt(&buf, Int32(4))
+
+
+ case .InvalidKeyLength:
+ writeInt(&buf, Int32(5))
+
+ }
+ }
+}
+
+
+extension CryptoError: Equatable, Hashable {}
+
+extension CryptoError: Foundation.LocalizedError {
+ public var errorDescription: String? {
+ String(reflecting: self)
+ }
+}
+
+
+public enum SyncError {
+
+
+
+ case Http
+ case Auth
+ case Conflict
+ case Parse
+}
+
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+public struct FfiConverterTypeSyncError: FfiConverterRustBuffer {
+ typealias SwiftType = SyncError
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SyncError {
+ let variant: Int32 = try readInt(&buf)
+ switch variant {
+
+
+
+
+ case 1: return .Http
+ case 2: return .Auth
+ case 3: return .Conflict
+ case 4: return .Parse
+
+ default: throw UniffiInternalError.unexpectedEnumCase
+ }
+ }
+
+ public static func write(_ value: SyncError, into buf: inout [UInt8]) {
+ switch value {
+
+
+
+
+
+ case .Http:
+ writeInt(&buf, Int32(1))
+
+
+ case .Auth:
+ writeInt(&buf, Int32(2))
+
+
+ case .Conflict:
+ writeInt(&buf, Int32(3))
+
+
+ case .Parse:
+ writeInt(&buf, Int32(4))
+
+ }
+ }
+}
+
+
+extension SyncError: Equatable, Hashable {}
+
+extension SyncError: Foundation.LocalizedError {
+ public var errorDescription: String? {
+ String(reflecting: self)
+ }
+}
+
+
+public enum VaultError {
+
+
+
+ case NotFound
+ case Serialization
+ case Crypto
+}
+
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+public struct FfiConverterTypeVaultError: FfiConverterRustBuffer {
+ typealias SwiftType = VaultError
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> VaultError {
+ let variant: Int32 = try readInt(&buf)
+ switch variant {
+
+
+
+
+ case 1: return .NotFound
+ case 2: return .Serialization
+ case 3: return .Crypto
+
+ default: throw UniffiInternalError.unexpectedEnumCase
+ }
+ }
+
+ public static func write(_ value: VaultError, into buf: inout [UInt8]) {
+ switch value {
+
+
+
+
+
+ case .NotFound:
+ writeInt(&buf, Int32(1))
+
+
+ case .Serialization:
+ writeInt(&buf, Int32(2))
+
+
+ case .Crypto:
+ writeInt(&buf, Int32(3))
+
+ }
+ }
+}
+
+
+extension VaultError: Equatable, Hashable {}
+
+extension VaultError: Foundation.LocalizedError {
+ public var errorDescription: String? {
+ String(reflecting: self)
+ }
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+fileprivate struct FfiConverterOptionBool: FfiConverterRustBuffer {
+ typealias SwiftType = Bool?
+
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
+ guard let value = value else {
+ writeInt(&buf, Int8(0))
+ return
+ }
+ writeInt(&buf, Int8(1))
+ FfiConverterBool.write(value, into: &buf)
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
+ switch try readInt(&buf) as Int8 {
+ case 0: return nil
+ case 1: return try FfiConverterBool.read(from: &buf)
+ default: throw UniffiInternalError.unexpectedOptionalTag
+ }
+ }
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+fileprivate struct FfiConverterOptionString: FfiConverterRustBuffer {
+ typealias SwiftType = String?
+
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
+ guard let value = value else {
+ writeInt(&buf, Int8(0))
+ return
+ }
+ writeInt(&buf, Int8(1))
+ FfiConverterString.write(value, into: &buf)
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
+ switch try readInt(&buf) as Int8 {
+ case 0: return nil
+ case 1: return try FfiConverterString.read(from: &buf)
+ default: throw UniffiInternalError.unexpectedOptionalTag
+ }
+ }
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+fileprivate struct FfiConverterOptionData: FfiConverterRustBuffer {
+ typealias SwiftType = Data?
+
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
+ guard let value = value else {
+ writeInt(&buf, Int8(0))
+ return
+ }
+ writeInt(&buf, Int8(1))
+ FfiConverterData.write(value, into: &buf)
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
+ switch try readInt(&buf) as Int8 {
+ case 0: return nil
+ case 1: return try FfiConverterData.read(from: &buf)
+ default: throw UniffiInternalError.unexpectedOptionalTag
+ }
+ }
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+fileprivate struct FfiConverterOptionTypeVaultItem: FfiConverterRustBuffer {
+ typealias SwiftType = VaultItem?
+
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
+ guard let value = value else {
+ writeInt(&buf, Int8(0))
+ return
+ }
+ writeInt(&buf, Int8(1))
+ FfiConverterTypeVaultItem.write(value, into: &buf)
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
+ switch try readInt(&buf) as Int8 {
+ case 0: return nil
+ case 1: return try FfiConverterTypeVaultItem.read(from: &buf)
+ default: throw UniffiInternalError.unexpectedOptionalTag
+ }
+ }
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+fileprivate struct FfiConverterOptionSequenceString: FfiConverterRustBuffer {
+ typealias SwiftType = [String]?
+
+ public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
+ guard let value = value else {
+ writeInt(&buf, Int8(0))
+ return
+ }
+ writeInt(&buf, Int8(1))
+ FfiConverterSequenceString.write(value, into: &buf)
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
+ switch try readInt(&buf) as Int8 {
+ case 0: return nil
+ case 1: return try FfiConverterSequenceString.read(from: &buf)
+ default: throw UniffiInternalError.unexpectedOptionalTag
+ }
+ }
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+fileprivate struct FfiConverterSequenceString: FfiConverterRustBuffer {
+ typealias SwiftType = [String]
+
+ public static func write(_ value: [String], into buf: inout [UInt8]) {
+ let len = Int32(value.count)
+ writeInt(&buf, len)
+ for item in value {
+ FfiConverterString.write(item, into: &buf)
+ }
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [String] {
+ let len: Int32 = try readInt(&buf)
+ var seq = [String]()
+ seq.reserveCapacity(Int(len))
+ for _ in 0 ..< len {
+ seq.append(try FfiConverterString.read(from: &buf))
+ }
+ return seq
+ }
+}
+
+#if swift(>=5.8)
+@_documentation(visibility: private)
+#endif
+fileprivate struct FfiConverterSequenceTypeVaultItem: FfiConverterRustBuffer {
+ typealias SwiftType = [VaultItem]
+
+ public static func write(_ value: [VaultItem], into buf: inout [UInt8]) {
+ let len = Int32(value.count)
+ writeInt(&buf, len)
+ for item in value {
+ FfiConverterTypeVaultItem.write(item, into: &buf)
+ }
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [VaultItem] {
+ let len: Int32 = try readInt(&buf)
+ var seq = [VaultItem]()
+ seq.reserveCapacity(Int(len))
+ for _ in 0 ..< len {
+ seq.append(try FfiConverterTypeVaultItem.read(from: &buf))
+ }
+ return seq
+ }
+}
+public func decrypt(ciphertext: Data, key: Data)throws -> Data {
+ return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeCryptoError.lift) {
+ uniffi_noro_mobile_core_fn_func_decrypt(
+ FfiConverterData.lower(ciphertext),
+ FfiConverterData.lower(key),$0
+ )
+})
+}
+public func deriveAuk(password: String, secretKey: String, salt: Data)throws -> Data {
+ return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeCryptoError.lift) {
+ uniffi_noro_mobile_core_fn_func_derive_auk(
+ FfiConverterString.lower(password),
+ FfiConverterString.lower(secretKey),
+ FfiConverterData.lower(salt),$0
+ )
+})
+}
+public func deriveItemKey(vaultKey: Data, itemId: String)throws -> Data {
+ return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeCryptoError.lift) {
+ uniffi_noro_mobile_core_fn_func_derive_item_key(
+ FfiConverterData.lower(vaultKey),
+ FfiConverterString.lower(itemId),$0
+ )
+})
+}
+public func encrypt(plaintext: Data, key: Data)throws -> Data {
+ return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeCryptoError.lift) {
+ uniffi_noro_mobile_core_fn_func_encrypt(
+ FfiConverterData.lower(plaintext),
+ FfiConverterData.lower(key),$0
+ )
+})
+}
+public func generateItemId() -> String {
+ return try! FfiConverterString.lift(try! rustCall() {
+ uniffi_noro_mobile_core_fn_func_generate_item_id($0
+ )
+})
+}
+public func generateSalt() -> Data {
+ return try! FfiConverterData.lift(try! rustCall() {
+ uniffi_noro_mobile_core_fn_func_generate_salt($0
+ )
+})
+}
+public func generateSecretKey() -> String {
+ return try! FfiConverterString.lift(try! rustCall() {
+ uniffi_noro_mobile_core_fn_func_generate_secret_key($0
+ )
+})
+}
+public func generateVaultKey() -> Data {
+ return try! FfiConverterData.lift(try! rustCall() {
+ uniffi_noro_mobile_core_fn_func_generate_vault_key($0
+ )
+})
+}
+public func unwrapVaultKey(wrapped: Data, auk: Data)throws -> Data {
+ return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeCryptoError.lift) {
+ uniffi_noro_mobile_core_fn_func_unwrap_vault_key(
+ FfiConverterData.lower(wrapped),
+ FfiConverterData.lower(auk),$0
+ )
+})
+}
+public func wrapVaultKey(vaultKey: Data, auk: Data)throws -> Data {
+ return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeCryptoError.lift) {
+ uniffi_noro_mobile_core_fn_func_wrap_vault_key(
+ FfiConverterData.lower(vaultKey),
+ FfiConverterData.lower(auk),$0
+ )
+})
+}
+
+private enum InitializationResult {
+ case ok
+ case contractVersionMismatch
+ case apiChecksumMismatch
+}
+// Use a global variable to perform the versioning checks. Swift ensures that
+// the code inside is only computed once.
+private var initializationResult: InitializationResult = {
+ // Get the bindings contract version from our ComponentInterface
+ let bindings_contract_version = 26
+ // Get the scaffolding contract version by calling the into the dylib
+ let scaffolding_contract_version = ffi_noro_mobile_core_uniffi_contract_version()
+ if bindings_contract_version != scaffolding_contract_version {
+ return InitializationResult.contractVersionMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_func_decrypt() != 32839) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_func_derive_auk() != 37186) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_func_derive_item_key() != 51281) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_func_encrypt() != 46133) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_func_generate_item_id() != 22417) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_func_generate_salt() != 31865) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_func_generate_secret_key() != 53137) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_func_generate_vault_key() != 50386) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_func_unwrap_vault_key() != 38585) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_func_wrap_vault_key() != 17122) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_method_syncclient_create_item() != 51847) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_method_syncclient_delete_item() != 22816) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_method_syncclient_fetch_items() != 53777) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_method_syncclient_login() != 19049) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_method_syncclient_set_token() != 22416) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_method_syncclient_update_item() != 45106) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_method_vault_create_item() != 60480) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_method_vault_delete_item() != 63195) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_method_vault_get_item() != 55932) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_method_vault_list_items() != 61447) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_method_vault_load() != 6499) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_method_vault_save() != 50962) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_method_vault_search_items() != 42826) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_method_vault_update_item() != 55533) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_constructor_syncclient_new() != 38165) {
+ return InitializationResult.apiChecksumMismatch
+ }
+ if (uniffi_noro_mobile_core_checksum_constructor_vault_new() != 9762) {
+ return InitializationResult.apiChecksumMismatch
+ }
+
+ return InitializationResult.ok
+}()
+
+private func uniffiEnsureInitialized() {
+ switch initializationResult {
+ case .ok:
+ break
+ case .contractVersionMismatch:
+ fatalError("UniFFI contract version mismatch: try cleaning and rebuilding your project")
+ case .apiChecksumMismatch:
+ fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+}
+
+// swiftlint:enable all
\ No newline at end of file
diff --git a/apps/mobile-core/bindings/NoroCoreFFI.h b/apps/mobile-core/bindings/NoroCoreFFI.h
new file mode 100644
index 0000000..8be282c
--- /dev/null
+++ b/apps/mobile-core/bindings/NoroCoreFFI.h
@@ -0,0 +1,851 @@
+// This file was autogenerated by some hot garbage in the `uniffi` crate.
+// Trust me, you don't want to mess with it!
+
+#pragma once
+
+#include
+#include
+#include
+
+// The following structs are used to implement the lowest level
+// of the FFI, and thus useful to multiple uniffied crates.
+// We ensure they are declared exactly once, with a header guard, UNIFFI_SHARED_H.
+#ifdef UNIFFI_SHARED_H
+ // We also try to prevent mixing versions of shared uniffi header structs.
+ // If you add anything to the #else block, you must increment the version suffix in UNIFFI_SHARED_HEADER_V4
+ #ifndef UNIFFI_SHARED_HEADER_V4
+ #error Combining helper code from multiple versions of uniffi is not supported
+ #endif // ndef UNIFFI_SHARED_HEADER_V4
+#else
+#define UNIFFI_SHARED_H
+#define UNIFFI_SHARED_HEADER_V4
+// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
+// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
+
+typedef struct RustBuffer
+{
+ uint64_t capacity;
+ uint64_t len;
+ uint8_t *_Nullable data;
+} RustBuffer;
+
+typedef struct ForeignBytes
+{
+ int32_t len;
+ const uint8_t *_Nullable data;
+} ForeignBytes;
+
+// Error definitions
+typedef struct RustCallStatus {
+ int8_t code;
+ RustBuffer errorBuf;
+} RustCallStatus;
+
+// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
+// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
+#endif // def UNIFFI_SHARED_H
+#ifndef UNIFFI_FFIDEF_RUST_FUTURE_CONTINUATION_CALLBACK
+#define UNIFFI_FFIDEF_RUST_FUTURE_CONTINUATION_CALLBACK
+typedef void (*UniffiRustFutureContinuationCallback)(uint64_t, int8_t
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_FREE
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_FREE
+typedef void (*UniffiForeignFutureFree)(uint64_t
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_CALLBACK_INTERFACE_FREE
+#define UNIFFI_FFIDEF_CALLBACK_INTERFACE_FREE
+typedef void (*UniffiCallbackInterfaceFree)(uint64_t
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE
+typedef struct UniffiForeignFuture {
+ uint64_t handle;
+ UniffiForeignFutureFree _Nonnull free;
+} UniffiForeignFuture;
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_U8
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_U8
+typedef struct UniffiForeignFutureStructU8 {
+ uint8_t returnValue;
+ RustCallStatus callStatus;
+} UniffiForeignFutureStructU8;
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U8
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U8
+typedef void (*UniffiForeignFutureCompleteU8)(uint64_t, UniffiForeignFutureStructU8
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_I8
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_I8
+typedef struct UniffiForeignFutureStructI8 {
+ int8_t returnValue;
+ RustCallStatus callStatus;
+} UniffiForeignFutureStructI8;
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I8
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I8
+typedef void (*UniffiForeignFutureCompleteI8)(uint64_t, UniffiForeignFutureStructI8
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_U16
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_U16
+typedef struct UniffiForeignFutureStructU16 {
+ uint16_t returnValue;
+ RustCallStatus callStatus;
+} UniffiForeignFutureStructU16;
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U16
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U16
+typedef void (*UniffiForeignFutureCompleteU16)(uint64_t, UniffiForeignFutureStructU16
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_I16
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_I16
+typedef struct UniffiForeignFutureStructI16 {
+ int16_t returnValue;
+ RustCallStatus callStatus;
+} UniffiForeignFutureStructI16;
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I16
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I16
+typedef void (*UniffiForeignFutureCompleteI16)(uint64_t, UniffiForeignFutureStructI16
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_U32
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_U32
+typedef struct UniffiForeignFutureStructU32 {
+ uint32_t returnValue;
+ RustCallStatus callStatus;
+} UniffiForeignFutureStructU32;
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U32
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U32
+typedef void (*UniffiForeignFutureCompleteU32)(uint64_t, UniffiForeignFutureStructU32
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_I32
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_I32
+typedef struct UniffiForeignFutureStructI32 {
+ int32_t returnValue;
+ RustCallStatus callStatus;
+} UniffiForeignFutureStructI32;
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I32
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I32
+typedef void (*UniffiForeignFutureCompleteI32)(uint64_t, UniffiForeignFutureStructI32
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_U64
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_U64
+typedef struct UniffiForeignFutureStructU64 {
+ uint64_t returnValue;
+ RustCallStatus callStatus;
+} UniffiForeignFutureStructU64;
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U64
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U64
+typedef void (*UniffiForeignFutureCompleteU64)(uint64_t, UniffiForeignFutureStructU64
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_I64
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_I64
+typedef struct UniffiForeignFutureStructI64 {
+ int64_t returnValue;
+ RustCallStatus callStatus;
+} UniffiForeignFutureStructI64;
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I64
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I64
+typedef void (*UniffiForeignFutureCompleteI64)(uint64_t, UniffiForeignFutureStructI64
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_F32
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_F32
+typedef struct UniffiForeignFutureStructF32 {
+ float returnValue;
+ RustCallStatus callStatus;
+} UniffiForeignFutureStructF32;
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F32
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F32
+typedef void (*UniffiForeignFutureCompleteF32)(uint64_t, UniffiForeignFutureStructF32
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_F64
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_F64
+typedef struct UniffiForeignFutureStructF64 {
+ double returnValue;
+ RustCallStatus callStatus;
+} UniffiForeignFutureStructF64;
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F64
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F64
+typedef void (*UniffiForeignFutureCompleteF64)(uint64_t, UniffiForeignFutureStructF64
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_POINTER
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_POINTER
+typedef struct UniffiForeignFutureStructPointer {
+ void*_Nonnull returnValue;
+ RustCallStatus callStatus;
+} UniffiForeignFutureStructPointer;
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_POINTER
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_POINTER
+typedef void (*UniffiForeignFutureCompletePointer)(uint64_t, UniffiForeignFutureStructPointer
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_RUST_BUFFER
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_RUST_BUFFER
+typedef struct UniffiForeignFutureStructRustBuffer {
+ RustBuffer returnValue;
+ RustCallStatus callStatus;
+} UniffiForeignFutureStructRustBuffer;
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_RUST_BUFFER
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_RUST_BUFFER
+typedef void (*UniffiForeignFutureCompleteRustBuffer)(uint64_t, UniffiForeignFutureStructRustBuffer
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_VOID
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_STRUCT_VOID
+typedef struct UniffiForeignFutureStructVoid {
+ RustCallStatus callStatus;
+} UniffiForeignFutureStructVoid;
+
+#endif
+#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_VOID
+#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_VOID
+typedef void (*UniffiForeignFutureCompleteVoid)(uint64_t, UniffiForeignFutureStructVoid
+ );
+
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_CLONE_SYNCCLIENT
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_CLONE_SYNCCLIENT
+void*_Nonnull uniffi_noro_mobile_core_fn_clone_syncclient(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FREE_SYNCCLIENT
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FREE_SYNCCLIENT
+void uniffi_noro_mobile_core_fn_free_syncclient(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_CONSTRUCTOR_SYNCCLIENT_NEW
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_CONSTRUCTOR_SYNCCLIENT_NEW
+void*_Nonnull uniffi_noro_mobile_core_fn_constructor_syncclient_new(RustBuffer base_url, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_SYNCCLIENT_CREATE_ITEM
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_SYNCCLIENT_CREATE_ITEM
+RustBuffer uniffi_noro_mobile_core_fn_method_syncclient_create_item(void*_Nonnull ptr, RustBuffer item_type, RustBuffer title, RustBuffer data, RustBuffer tags, int8_t favorite, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_SYNCCLIENT_DELETE_ITEM
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_SYNCCLIENT_DELETE_ITEM
+void uniffi_noro_mobile_core_fn_method_syncclient_delete_item(void*_Nonnull ptr, RustBuffer id, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_SYNCCLIENT_FETCH_ITEMS
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_SYNCCLIENT_FETCH_ITEMS
+RustBuffer uniffi_noro_mobile_core_fn_method_syncclient_fetch_items(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_SYNCCLIENT_LOGIN
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_SYNCCLIENT_LOGIN
+RustBuffer uniffi_noro_mobile_core_fn_method_syncclient_login(void*_Nonnull ptr, RustBuffer email, RustBuffer password, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_SYNCCLIENT_SET_TOKEN
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_SYNCCLIENT_SET_TOKEN
+void uniffi_noro_mobile_core_fn_method_syncclient_set_token(void*_Nonnull ptr, RustBuffer token, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_SYNCCLIENT_UPDATE_ITEM
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_SYNCCLIENT_UPDATE_ITEM
+RustBuffer uniffi_noro_mobile_core_fn_method_syncclient_update_item(void*_Nonnull ptr, RustBuffer id, RustBuffer title, RustBuffer data, RustBuffer tags, RustBuffer favorite, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_CLONE_VAULT
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_CLONE_VAULT
+void*_Nonnull uniffi_noro_mobile_core_fn_clone_vault(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FREE_VAULT
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FREE_VAULT
+void uniffi_noro_mobile_core_fn_free_vault(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_CONSTRUCTOR_VAULT_NEW
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_CONSTRUCTOR_VAULT_NEW
+void*_Nonnull uniffi_noro_mobile_core_fn_constructor_vault_new(RustCallStatus *_Nonnull out_status
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_CREATE_ITEM
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_CREATE_ITEM
+RustBuffer uniffi_noro_mobile_core_fn_method_vault_create_item(void*_Nonnull ptr, RustBuffer item_type, RustBuffer title, RustBuffer data, RustBuffer tags, int8_t favorite, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_DELETE_ITEM
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_DELETE_ITEM
+void uniffi_noro_mobile_core_fn_method_vault_delete_item(void*_Nonnull ptr, RustBuffer id, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_GET_ITEM
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_GET_ITEM
+RustBuffer uniffi_noro_mobile_core_fn_method_vault_get_item(void*_Nonnull ptr, RustBuffer id, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_LIST_ITEMS
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_LIST_ITEMS
+RustBuffer uniffi_noro_mobile_core_fn_method_vault_list_items(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_LOAD
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_LOAD
+void uniffi_noro_mobile_core_fn_method_vault_load(void*_Nonnull ptr, RustBuffer encrypted, RustBuffer key, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_SAVE
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_SAVE
+RustBuffer uniffi_noro_mobile_core_fn_method_vault_save(void*_Nonnull ptr, RustBuffer key, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_SEARCH_ITEMS
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_SEARCH_ITEMS
+RustBuffer uniffi_noro_mobile_core_fn_method_vault_search_items(void*_Nonnull ptr, RustBuffer query, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_UPDATE_ITEM
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_METHOD_VAULT_UPDATE_ITEM
+RustBuffer uniffi_noro_mobile_core_fn_method_vault_update_item(void*_Nonnull ptr, RustBuffer id, RustBuffer title, RustBuffer data, RustBuffer tags, RustBuffer favorite, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_DECRYPT
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_DECRYPT
+RustBuffer uniffi_noro_mobile_core_fn_func_decrypt(RustBuffer ciphertext, RustBuffer key, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_DERIVE_AUK
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_DERIVE_AUK
+RustBuffer uniffi_noro_mobile_core_fn_func_derive_auk(RustBuffer password, RustBuffer secret_key, RustBuffer salt, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_DERIVE_ITEM_KEY
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_DERIVE_ITEM_KEY
+RustBuffer uniffi_noro_mobile_core_fn_func_derive_item_key(RustBuffer vault_key, RustBuffer item_id, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_ENCRYPT
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_ENCRYPT
+RustBuffer uniffi_noro_mobile_core_fn_func_encrypt(RustBuffer plaintext, RustBuffer key, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_GENERATE_ITEM_ID
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_GENERATE_ITEM_ID
+RustBuffer uniffi_noro_mobile_core_fn_func_generate_item_id(RustCallStatus *_Nonnull out_status
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_GENERATE_SALT
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_GENERATE_SALT
+RustBuffer uniffi_noro_mobile_core_fn_func_generate_salt(RustCallStatus *_Nonnull out_status
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_GENERATE_SECRET_KEY
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_GENERATE_SECRET_KEY
+RustBuffer uniffi_noro_mobile_core_fn_func_generate_secret_key(RustCallStatus *_Nonnull out_status
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_GENERATE_VAULT_KEY
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_GENERATE_VAULT_KEY
+RustBuffer uniffi_noro_mobile_core_fn_func_generate_vault_key(RustCallStatus *_Nonnull out_status
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_UNWRAP_VAULT_KEY
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_UNWRAP_VAULT_KEY
+RustBuffer uniffi_noro_mobile_core_fn_func_unwrap_vault_key(RustBuffer wrapped, RustBuffer auk, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_WRAP_VAULT_KEY
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_FN_FUNC_WRAP_VAULT_KEY
+RustBuffer uniffi_noro_mobile_core_fn_func_wrap_vault_key(RustBuffer vault_key, RustBuffer auk, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUSTBUFFER_ALLOC
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUSTBUFFER_ALLOC
+RustBuffer ffi_noro_mobile_core_rustbuffer_alloc(uint64_t size, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUSTBUFFER_FROM_BYTES
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUSTBUFFER_FROM_BYTES
+RustBuffer ffi_noro_mobile_core_rustbuffer_from_bytes(ForeignBytes bytes, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUSTBUFFER_FREE
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUSTBUFFER_FREE
+void ffi_noro_mobile_core_rustbuffer_free(RustBuffer buf, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUSTBUFFER_RESERVE
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUSTBUFFER_RESERVE
+RustBuffer ffi_noro_mobile_core_rustbuffer_reserve(RustBuffer buf, uint64_t additional, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_U8
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_U8
+void ffi_noro_mobile_core_rust_future_poll_u8(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_U8
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_U8
+void ffi_noro_mobile_core_rust_future_cancel_u8(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_U8
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_U8
+void ffi_noro_mobile_core_rust_future_free_u8(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_U8
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_U8
+uint8_t ffi_noro_mobile_core_rust_future_complete_u8(uint64_t handle, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_I8
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_I8
+void ffi_noro_mobile_core_rust_future_poll_i8(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_I8
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_I8
+void ffi_noro_mobile_core_rust_future_cancel_i8(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_I8
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_I8
+void ffi_noro_mobile_core_rust_future_free_i8(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_I8
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_I8
+int8_t ffi_noro_mobile_core_rust_future_complete_i8(uint64_t handle, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_U16
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_U16
+void ffi_noro_mobile_core_rust_future_poll_u16(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_U16
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_U16
+void ffi_noro_mobile_core_rust_future_cancel_u16(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_U16
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_U16
+void ffi_noro_mobile_core_rust_future_free_u16(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_U16
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_U16
+uint16_t ffi_noro_mobile_core_rust_future_complete_u16(uint64_t handle, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_I16
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_I16
+void ffi_noro_mobile_core_rust_future_poll_i16(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_I16
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_I16
+void ffi_noro_mobile_core_rust_future_cancel_i16(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_I16
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_I16
+void ffi_noro_mobile_core_rust_future_free_i16(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_I16
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_I16
+int16_t ffi_noro_mobile_core_rust_future_complete_i16(uint64_t handle, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_U32
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_U32
+void ffi_noro_mobile_core_rust_future_poll_u32(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_U32
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_U32
+void ffi_noro_mobile_core_rust_future_cancel_u32(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_U32
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_U32
+void ffi_noro_mobile_core_rust_future_free_u32(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_U32
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_U32
+uint32_t ffi_noro_mobile_core_rust_future_complete_u32(uint64_t handle, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_I32
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_I32
+void ffi_noro_mobile_core_rust_future_poll_i32(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_I32
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_I32
+void ffi_noro_mobile_core_rust_future_cancel_i32(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_I32
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_I32
+void ffi_noro_mobile_core_rust_future_free_i32(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_I32
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_I32
+int32_t ffi_noro_mobile_core_rust_future_complete_i32(uint64_t handle, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_U64
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_U64
+void ffi_noro_mobile_core_rust_future_poll_u64(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_U64
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_U64
+void ffi_noro_mobile_core_rust_future_cancel_u64(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_U64
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_U64
+void ffi_noro_mobile_core_rust_future_free_u64(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_U64
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_U64
+uint64_t ffi_noro_mobile_core_rust_future_complete_u64(uint64_t handle, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_I64
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_I64
+void ffi_noro_mobile_core_rust_future_poll_i64(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_I64
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_I64
+void ffi_noro_mobile_core_rust_future_cancel_i64(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_I64
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_I64
+void ffi_noro_mobile_core_rust_future_free_i64(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_I64
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_I64
+int64_t ffi_noro_mobile_core_rust_future_complete_i64(uint64_t handle, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_F32
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_F32
+void ffi_noro_mobile_core_rust_future_poll_f32(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_F32
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_F32
+void ffi_noro_mobile_core_rust_future_cancel_f32(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_F32
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_F32
+void ffi_noro_mobile_core_rust_future_free_f32(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_F32
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_F32
+float ffi_noro_mobile_core_rust_future_complete_f32(uint64_t handle, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_F64
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_F64
+void ffi_noro_mobile_core_rust_future_poll_f64(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_F64
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_F64
+void ffi_noro_mobile_core_rust_future_cancel_f64(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_F64
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_F64
+void ffi_noro_mobile_core_rust_future_free_f64(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_F64
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_F64
+double ffi_noro_mobile_core_rust_future_complete_f64(uint64_t handle, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_POINTER
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_POINTER
+void ffi_noro_mobile_core_rust_future_poll_pointer(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_POINTER
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_POINTER
+void ffi_noro_mobile_core_rust_future_cancel_pointer(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_POINTER
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_POINTER
+void ffi_noro_mobile_core_rust_future_free_pointer(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_POINTER
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_POINTER
+void*_Nonnull ffi_noro_mobile_core_rust_future_complete_pointer(uint64_t handle, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_RUST_BUFFER
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_RUST_BUFFER
+void ffi_noro_mobile_core_rust_future_poll_rust_buffer(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_RUST_BUFFER
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_RUST_BUFFER
+void ffi_noro_mobile_core_rust_future_cancel_rust_buffer(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_RUST_BUFFER
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_RUST_BUFFER
+void ffi_noro_mobile_core_rust_future_free_rust_buffer(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_RUST_BUFFER
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_RUST_BUFFER
+RustBuffer ffi_noro_mobile_core_rust_future_complete_rust_buffer(uint64_t handle, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_VOID
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_POLL_VOID
+void ffi_noro_mobile_core_rust_future_poll_void(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_VOID
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_CANCEL_VOID
+void ffi_noro_mobile_core_rust_future_cancel_void(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_VOID
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_FREE_VOID
+void ffi_noro_mobile_core_rust_future_free_void(uint64_t handle
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_VOID
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_RUST_FUTURE_COMPLETE_VOID
+void ffi_noro_mobile_core_rust_future_complete_void(uint64_t handle, RustCallStatus *_Nonnull out_status
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_DECRYPT
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_DECRYPT
+uint16_t uniffi_noro_mobile_core_checksum_func_decrypt(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_DERIVE_AUK
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_DERIVE_AUK
+uint16_t uniffi_noro_mobile_core_checksum_func_derive_auk(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_DERIVE_ITEM_KEY
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_DERIVE_ITEM_KEY
+uint16_t uniffi_noro_mobile_core_checksum_func_derive_item_key(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_ENCRYPT
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_ENCRYPT
+uint16_t uniffi_noro_mobile_core_checksum_func_encrypt(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_GENERATE_ITEM_ID
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_GENERATE_ITEM_ID
+uint16_t uniffi_noro_mobile_core_checksum_func_generate_item_id(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_GENERATE_SALT
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_GENERATE_SALT
+uint16_t uniffi_noro_mobile_core_checksum_func_generate_salt(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_GENERATE_SECRET_KEY
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_GENERATE_SECRET_KEY
+uint16_t uniffi_noro_mobile_core_checksum_func_generate_secret_key(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_GENERATE_VAULT_KEY
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_GENERATE_VAULT_KEY
+uint16_t uniffi_noro_mobile_core_checksum_func_generate_vault_key(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_UNWRAP_VAULT_KEY
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_UNWRAP_VAULT_KEY
+uint16_t uniffi_noro_mobile_core_checksum_func_unwrap_vault_key(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_WRAP_VAULT_KEY
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_FUNC_WRAP_VAULT_KEY
+uint16_t uniffi_noro_mobile_core_checksum_func_wrap_vault_key(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_SYNCCLIENT_CREATE_ITEM
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_SYNCCLIENT_CREATE_ITEM
+uint16_t uniffi_noro_mobile_core_checksum_method_syncclient_create_item(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_SYNCCLIENT_DELETE_ITEM
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_SYNCCLIENT_DELETE_ITEM
+uint16_t uniffi_noro_mobile_core_checksum_method_syncclient_delete_item(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_SYNCCLIENT_FETCH_ITEMS
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_SYNCCLIENT_FETCH_ITEMS
+uint16_t uniffi_noro_mobile_core_checksum_method_syncclient_fetch_items(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_SYNCCLIENT_LOGIN
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_SYNCCLIENT_LOGIN
+uint16_t uniffi_noro_mobile_core_checksum_method_syncclient_login(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_SYNCCLIENT_SET_TOKEN
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_SYNCCLIENT_SET_TOKEN
+uint16_t uniffi_noro_mobile_core_checksum_method_syncclient_set_token(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_SYNCCLIENT_UPDATE_ITEM
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_SYNCCLIENT_UPDATE_ITEM
+uint16_t uniffi_noro_mobile_core_checksum_method_syncclient_update_item(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_CREATE_ITEM
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_CREATE_ITEM
+uint16_t uniffi_noro_mobile_core_checksum_method_vault_create_item(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_DELETE_ITEM
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_DELETE_ITEM
+uint16_t uniffi_noro_mobile_core_checksum_method_vault_delete_item(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_GET_ITEM
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_GET_ITEM
+uint16_t uniffi_noro_mobile_core_checksum_method_vault_get_item(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_LIST_ITEMS
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_LIST_ITEMS
+uint16_t uniffi_noro_mobile_core_checksum_method_vault_list_items(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_LOAD
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_LOAD
+uint16_t uniffi_noro_mobile_core_checksum_method_vault_load(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_SAVE
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_SAVE
+uint16_t uniffi_noro_mobile_core_checksum_method_vault_save(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_SEARCH_ITEMS
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_SEARCH_ITEMS
+uint16_t uniffi_noro_mobile_core_checksum_method_vault_search_items(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_UPDATE_ITEM
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_METHOD_VAULT_UPDATE_ITEM
+uint16_t uniffi_noro_mobile_core_checksum_method_vault_update_item(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_CONSTRUCTOR_SYNCCLIENT_NEW
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_CONSTRUCTOR_SYNCCLIENT_NEW
+uint16_t uniffi_noro_mobile_core_checksum_constructor_syncclient_new(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_CONSTRUCTOR_VAULT_NEW
+#define UNIFFI_FFIDEF_UNIFFI_NORO_MOBILE_CORE_CHECKSUM_CONSTRUCTOR_VAULT_NEW
+uint16_t uniffi_noro_mobile_core_checksum_constructor_vault_new(void
+
+);
+#endif
+#ifndef UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_UNIFFI_CONTRACT_VERSION
+#define UNIFFI_FFIDEF_FFI_NORO_MOBILE_CORE_UNIFFI_CONTRACT_VERSION
+uint32_t ffi_noro_mobile_core_uniffi_contract_version(void
+
+);
+#endif
+
diff --git a/apps/mobile-core/bindings/NoroCoreFFI.modulemap b/apps/mobile-core/bindings/NoroCoreFFI.modulemap
new file mode 100644
index 0000000..fdea99e
--- /dev/null
+++ b/apps/mobile-core/bindings/NoroCoreFFI.modulemap
@@ -0,0 +1,4 @@
+module NoroCoreFFI {
+ header "NoroCoreFFI.h"
+ export *
+}
\ No newline at end of file
diff --git a/apps/mobile-core/bindings/sh/noro/core/noro_mobile_core.kt b/apps/mobile-core/bindings/sh/noro/core/noro_mobile_core.kt
new file mode 100644
index 0000000..75a65b5
--- /dev/null
+++ b/apps/mobile-core/bindings/sh/noro/core/noro_mobile_core.kt
@@ -0,0 +1,2724 @@
+// This file was autogenerated by some hot garbage in the `uniffi` crate.
+// Trust me, you don't want to mess with it!
+
+@file:Suppress("NAME_SHADOWING")
+
+package sh.noro.core
+
+// Common helper code.
+//
+// Ideally this would live in a separate .kt file where it can be unittested etc
+// in isolation, and perhaps even published as a re-useable package.
+//
+// However, it's important that the details of how this helper code works (e.g. the
+// way that different builtin types are passed across the FFI) exactly match what's
+// expected by the Rust code on the other side of the interface. In practice right
+// now that means coming from the exact some version of `uniffi` that was used to
+// compile the Rust component. The easiest way to ensure this is to bundle the Kotlin
+// helpers directly inline like we're doing here.
+
+import com.sun.jna.Library
+import com.sun.jna.IntegerType
+import com.sun.jna.Native
+import com.sun.jna.Pointer
+import com.sun.jna.Structure
+import com.sun.jna.Callback
+import com.sun.jna.ptr.*
+import java.nio.ByteBuffer
+import java.nio.ByteOrder
+import java.nio.CharBuffer
+import java.nio.charset.CodingErrorAction
+import java.util.concurrent.atomic.AtomicLong
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.atomic.AtomicBoolean
+
+// This is a helper for safely working with byte buffers returned from the Rust code.
+// A rust-owned buffer is represented by its capacity, its current length, and a
+// pointer to the underlying data.
+
+/**
+ * @suppress
+ */
+@Structure.FieldOrder("capacity", "len", "data")
+open class RustBuffer : Structure() {
+ // Note: `capacity` and `len` are actually `ULong` values, but JVM only supports signed values.
+ // When dealing with these fields, make sure to call `toULong()`.
+ @JvmField var capacity: Long = 0
+ @JvmField var len: Long = 0
+ @JvmField var data: Pointer? = null
+
+ class ByValue: RustBuffer(), Structure.ByValue
+ class ByReference: RustBuffer(), Structure.ByReference
+
+ internal fun setValue(other: RustBuffer) {
+ capacity = other.capacity
+ len = other.len
+ data = other.data
+ }
+
+ companion object {
+ internal fun alloc(size: ULong = 0UL) = uniffiRustCall() { status ->
+ // Note: need to convert the size to a `Long` value to make this work with JVM.
+ UniffiLib.INSTANCE.ffi_noro_mobile_core_rustbuffer_alloc(size.toLong(), status)
+ }.also {
+ if(it.data == null) {
+ throw RuntimeException("RustBuffer.alloc() returned null data pointer (size=${size})")
+ }
+ }
+
+ internal fun create(capacity: ULong, len: ULong, data: Pointer?): RustBuffer.ByValue {
+ var buf = RustBuffer.ByValue()
+ buf.capacity = capacity.toLong()
+ buf.len = len.toLong()
+ buf.data = data
+ return buf
+ }
+
+ internal fun free(buf: RustBuffer.ByValue) = uniffiRustCall() { status ->
+ UniffiLib.INSTANCE.ffi_noro_mobile_core_rustbuffer_free(buf, status)
+ }
+ }
+
+ @Suppress("TooGenericExceptionThrown")
+ fun asByteBuffer() =
+ this.data?.getByteBuffer(0, this.len.toLong())?.also {
+ it.order(ByteOrder.BIG_ENDIAN)
+ }
+}
+
+/**
+ * The equivalent of the `*mut RustBuffer` type.
+ * Required for callbacks taking in an out pointer.
+ *
+ * Size is the sum of all values in the struct.
+ *
+ * @suppress
+ */
+class RustBufferByReference : ByReference(16) {
+ /**
+ * Set the pointed-to `RustBuffer` to the given value.
+ */
+ fun setValue(value: RustBuffer.ByValue) {
+ // NOTE: The offsets are as they are in the C-like struct.
+ val pointer = getPointer()
+ pointer.setLong(0, value.capacity)
+ pointer.setLong(8, value.len)
+ pointer.setPointer(16, value.data)
+ }
+
+ /**
+ * Get a `RustBuffer.ByValue` from this reference.
+ */
+ fun getValue(): RustBuffer.ByValue {
+ val pointer = getPointer()
+ val value = RustBuffer.ByValue()
+ value.writeField("capacity", pointer.getLong(0))
+ value.writeField("len", pointer.getLong(8))
+ value.writeField("data", pointer.getLong(16))
+
+ return value
+ }
+}
+
+// This is a helper for safely passing byte references into the rust code.
+// It's not actually used at the moment, because there aren't many things that you
+// can take a direct pointer to in the JVM, and if we're going to copy something
+// then we might as well copy it into a `RustBuffer`. But it's here for API
+// completeness.
+
+@Structure.FieldOrder("len", "data")
+internal open class ForeignBytes : Structure() {
+ @JvmField var len: Int = 0
+ @JvmField var data: Pointer? = null
+
+ class ByValue : ForeignBytes(), Structure.ByValue
+}
+/**
+ * The FfiConverter interface handles converter types to and from the FFI
+ *
+ * All implementing objects should be public to support external types. When a
+ * type is external we need to import it's FfiConverter.
+ *
+ * @suppress
+ */
+public interface FfiConverter {
+ // Convert an FFI type to a Kotlin type
+ fun lift(value: FfiType): KotlinType
+
+ // Convert an Kotlin type to an FFI type
+ fun lower(value: KotlinType): FfiType
+
+ // Read a Kotlin type from a `ByteBuffer`
+ fun read(buf: ByteBuffer): KotlinType
+
+ // Calculate bytes to allocate when creating a `RustBuffer`
+ //
+ // This must return at least as many bytes as the write() function will
+ // write. It can return more bytes than needed, for example when writing
+ // Strings we can't know the exact bytes needed until we the UTF-8
+ // encoding, so we pessimistically allocate the largest size possible (3
+ // bytes per codepoint). Allocating extra bytes is not really a big deal
+ // because the `RustBuffer` is short-lived.
+ fun allocationSize(value: KotlinType): ULong
+
+ // Write a Kotlin type to a `ByteBuffer`
+ fun write(value: KotlinType, buf: ByteBuffer)
+
+ // Lower a value into a `RustBuffer`
+ //
+ // This method lowers a value into a `RustBuffer` rather than the normal
+ // FfiType. It's used by the callback interface code. Callback interface
+ // returns are always serialized into a `RustBuffer` regardless of their
+ // normal FFI type.
+ fun lowerIntoRustBuffer(value: KotlinType): RustBuffer.ByValue {
+ val rbuf = RustBuffer.alloc(allocationSize(value))
+ try {
+ val bbuf = rbuf.data!!.getByteBuffer(0, rbuf.capacity).also {
+ it.order(ByteOrder.BIG_ENDIAN)
+ }
+ write(value, bbuf)
+ rbuf.writeField("len", bbuf.position().toLong())
+ return rbuf
+ } catch (e: Throwable) {
+ RustBuffer.free(rbuf)
+ throw e
+ }
+ }
+
+ // Lift a value from a `RustBuffer`.
+ //
+ // This here mostly because of the symmetry with `lowerIntoRustBuffer()`.
+ // It's currently only used by the `FfiConverterRustBuffer` class below.
+ fun liftFromRustBuffer(rbuf: RustBuffer.ByValue): KotlinType {
+ val byteBuf = rbuf.asByteBuffer()!!
+ try {
+ val item = read(byteBuf)
+ if (byteBuf.hasRemaining()) {
+ throw RuntimeException("junk remaining in buffer after lifting, something is very wrong!!")
+ }
+ return item
+ } finally {
+ RustBuffer.free(rbuf)
+ }
+ }
+}
+
+/**
+ * FfiConverter that uses `RustBuffer` as the FfiType
+ *
+ * @suppress
+ */
+public interface FfiConverterRustBuffer: FfiConverter {
+ override fun lift(value: RustBuffer.ByValue) = liftFromRustBuffer(value)
+ override fun lower(value: KotlinType) = lowerIntoRustBuffer(value)
+}
+// A handful of classes and functions to support the generated data structures.
+// This would be a good candidate for isolating in its own ffi-support lib.
+
+internal const val UNIFFI_CALL_SUCCESS = 0.toByte()
+internal const val UNIFFI_CALL_ERROR = 1.toByte()
+internal const val UNIFFI_CALL_UNEXPECTED_ERROR = 2.toByte()
+
+@Structure.FieldOrder("code", "error_buf")
+internal open class UniffiRustCallStatus : Structure() {
+ @JvmField var code: Byte = 0
+ @JvmField var error_buf: RustBuffer.ByValue = RustBuffer.ByValue()
+
+ class ByValue: UniffiRustCallStatus(), Structure.ByValue
+
+ fun isSuccess(): Boolean {
+ return code == UNIFFI_CALL_SUCCESS
+ }
+
+ fun isError(): Boolean {
+ return code == UNIFFI_CALL_ERROR
+ }
+
+ fun isPanic(): Boolean {
+ return code == UNIFFI_CALL_UNEXPECTED_ERROR
+ }
+
+ companion object {
+ fun create(code: Byte, errorBuf: RustBuffer.ByValue): UniffiRustCallStatus.ByValue {
+ val callStatus = UniffiRustCallStatus.ByValue()
+ callStatus.code = code
+ callStatus.error_buf = errorBuf
+ return callStatus
+ }
+ }
+}
+
+class InternalException(message: String) : kotlin.Exception(message)
+
+/**
+ * Each top-level error class has a companion object that can lift the error from the call status's rust buffer
+ *
+ * @suppress
+ */
+interface UniffiRustCallStatusErrorHandler {
+ fun lift(error_buf: RustBuffer.ByValue): E;
+}
+
+// Helpers for calling Rust
+// In practice we usually need to be synchronized to call this safely, so it doesn't
+// synchronize itself
+
+// Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err
+private inline fun uniffiRustCallWithError(errorHandler: UniffiRustCallStatusErrorHandler, callback: (UniffiRustCallStatus) -> U): U {
+ var status = UniffiRustCallStatus()
+ val return_value = callback(status)
+ uniffiCheckCallStatus(errorHandler, status)
+ return return_value
+}
+
+// Check UniffiRustCallStatus and throw an error if the call wasn't successful
+private fun uniffiCheckCallStatus(errorHandler: UniffiRustCallStatusErrorHandler, status: UniffiRustCallStatus) {
+ if (status.isSuccess()) {
+ return
+ } else if (status.isError()) {
+ throw errorHandler.lift(status.error_buf)
+ } else if (status.isPanic()) {
+ // when the rust code sees a panic, it tries to construct a rustbuffer
+ // with the message. but if that code panics, then it just sends back
+ // an empty buffer.
+ if (status.error_buf.len > 0) {
+ throw InternalException(FfiConverterString.lift(status.error_buf))
+ } else {
+ throw InternalException("Rust panic")
+ }
+ } else {
+ throw InternalException("Unknown rust call status: $status.code")
+ }
+}
+
+/**
+ * UniffiRustCallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR
+ *
+ * @suppress
+ */
+object UniffiNullRustCallStatusErrorHandler: UniffiRustCallStatusErrorHandler {
+ override fun lift(error_buf: RustBuffer.ByValue): InternalException {
+ RustBuffer.free(error_buf)
+ return InternalException("Unexpected CALL_ERROR")
+ }
+}
+
+// Call a rust function that returns a plain value
+private inline fun uniffiRustCall(callback: (UniffiRustCallStatus) -> U): U {
+ return uniffiRustCallWithError(UniffiNullRustCallStatusErrorHandler, callback)
+}
+
+internal inline fun uniffiTraitInterfaceCall(
+ callStatus: UniffiRustCallStatus,
+ makeCall: () -> T,
+ writeReturn: (T) -> Unit,
+) {
+ try {
+ writeReturn(makeCall())
+ } catch(e: kotlin.Exception) {
+ callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR
+ callStatus.error_buf = FfiConverterString.lower(e.toString())
+ }
+}
+
+internal inline fun uniffiTraitInterfaceCallWithError(
+ callStatus: UniffiRustCallStatus,
+ makeCall: () -> T,
+ writeReturn: (T) -> Unit,
+ lowerError: (E) -> RustBuffer.ByValue
+) {
+ try {
+ writeReturn(makeCall())
+ } catch(e: kotlin.Exception) {
+ if (e is E) {
+ callStatus.code = UNIFFI_CALL_ERROR
+ callStatus.error_buf = lowerError(e)
+ } else {
+ callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR
+ callStatus.error_buf = FfiConverterString.lower(e.toString())
+ }
+ }
+}
+// Map handles to objects
+//
+// This is used pass an opaque 64-bit handle representing a foreign object to the Rust code.
+internal class UniffiHandleMap {
+ private val map = ConcurrentHashMap()
+ private val counter = java.util.concurrent.atomic.AtomicLong(0)
+
+ val size: Int
+ get() = map.size
+
+ // Insert a new object into the handle map and get a handle for it
+ fun insert(obj: T): Long {
+ val handle = counter.getAndAdd(1)
+ map.put(handle, obj)
+ return handle
+ }
+
+ // Get an object from the handle map
+ fun get(handle: Long): T {
+ return map.get(handle) ?: throw InternalException("UniffiHandleMap.get: Invalid handle")
+ }
+
+ // Remove an entry from the handlemap and get the Kotlin object back
+ fun remove(handle: Long): T {
+ return map.remove(handle) ?: throw InternalException("UniffiHandleMap: Invalid handle")
+ }
+}
+
+// Contains loading, initialization code,
+// and the FFI Function declarations in a com.sun.jna.Library.
+@Synchronized
+private fun findLibraryName(componentName: String): String {
+ val libOverride = System.getProperty("uniffi.component.$componentName.libraryOverride")
+ if (libOverride != null) {
+ return libOverride
+ }
+ return "noro_mobile_core"
+}
+
+private inline fun loadIndirect(
+ componentName: String
+): Lib {
+ return Native.load(findLibraryName(componentName), Lib::class.java)
+}
+
+// Define FFI callback types
+internal interface UniffiRustFutureContinuationCallback : com.sun.jna.Callback {
+ fun callback(`data`: Long,`pollResult`: Byte,)
+}
+internal interface UniffiForeignFutureFree : com.sun.jna.Callback {
+ fun callback(`handle`: Long,)
+}
+internal interface UniffiCallbackInterfaceFree : com.sun.jna.Callback {
+ fun callback(`handle`: Long,)
+}
+@Structure.FieldOrder("handle", "free")
+internal open class UniffiForeignFuture(
+ @JvmField internal var `handle`: Long = 0.toLong(),
+ @JvmField internal var `free`: UniffiForeignFutureFree? = null,
+) : Structure() {
+ class UniffiByValue(
+ `handle`: Long = 0.toLong(),
+ `free`: UniffiForeignFutureFree? = null,
+ ): UniffiForeignFuture(`handle`,`free`,), Structure.ByValue
+
+ internal fun uniffiSetValue(other: UniffiForeignFuture) {
+ `handle` = other.`handle`
+ `free` = other.`free`
+ }
+
+}
+@Structure.FieldOrder("returnValue", "callStatus")
+internal open class UniffiForeignFutureStructU8(
+ @JvmField internal var `returnValue`: Byte = 0.toByte(),
+ @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+) : Structure() {
+ class UniffiByValue(
+ `returnValue`: Byte = 0.toByte(),
+ `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+ ): UniffiForeignFutureStructU8(`returnValue`,`callStatus`,), Structure.ByValue
+
+ internal fun uniffiSetValue(other: UniffiForeignFutureStructU8) {
+ `returnValue` = other.`returnValue`
+ `callStatus` = other.`callStatus`
+ }
+
+}
+internal interface UniffiForeignFutureCompleteU8 : com.sun.jna.Callback {
+ fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU8.UniffiByValue,)
+}
+@Structure.FieldOrder("returnValue", "callStatus")
+internal open class UniffiForeignFutureStructI8(
+ @JvmField internal var `returnValue`: Byte = 0.toByte(),
+ @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+) : Structure() {
+ class UniffiByValue(
+ `returnValue`: Byte = 0.toByte(),
+ `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+ ): UniffiForeignFutureStructI8(`returnValue`,`callStatus`,), Structure.ByValue
+
+ internal fun uniffiSetValue(other: UniffiForeignFutureStructI8) {
+ `returnValue` = other.`returnValue`
+ `callStatus` = other.`callStatus`
+ }
+
+}
+internal interface UniffiForeignFutureCompleteI8 : com.sun.jna.Callback {
+ fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI8.UniffiByValue,)
+}
+@Structure.FieldOrder("returnValue", "callStatus")
+internal open class UniffiForeignFutureStructU16(
+ @JvmField internal var `returnValue`: Short = 0.toShort(),
+ @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+) : Structure() {
+ class UniffiByValue(
+ `returnValue`: Short = 0.toShort(),
+ `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+ ): UniffiForeignFutureStructU16(`returnValue`,`callStatus`,), Structure.ByValue
+
+ internal fun uniffiSetValue(other: UniffiForeignFutureStructU16) {
+ `returnValue` = other.`returnValue`
+ `callStatus` = other.`callStatus`
+ }
+
+}
+internal interface UniffiForeignFutureCompleteU16 : com.sun.jna.Callback {
+ fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU16.UniffiByValue,)
+}
+@Structure.FieldOrder("returnValue", "callStatus")
+internal open class UniffiForeignFutureStructI16(
+ @JvmField internal var `returnValue`: Short = 0.toShort(),
+ @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+) : Structure() {
+ class UniffiByValue(
+ `returnValue`: Short = 0.toShort(),
+ `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+ ): UniffiForeignFutureStructI16(`returnValue`,`callStatus`,), Structure.ByValue
+
+ internal fun uniffiSetValue(other: UniffiForeignFutureStructI16) {
+ `returnValue` = other.`returnValue`
+ `callStatus` = other.`callStatus`
+ }
+
+}
+internal interface UniffiForeignFutureCompleteI16 : com.sun.jna.Callback {
+ fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI16.UniffiByValue,)
+}
+@Structure.FieldOrder("returnValue", "callStatus")
+internal open class UniffiForeignFutureStructU32(
+ @JvmField internal var `returnValue`: Int = 0,
+ @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+) : Structure() {
+ class UniffiByValue(
+ `returnValue`: Int = 0,
+ `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+ ): UniffiForeignFutureStructU32(`returnValue`,`callStatus`,), Structure.ByValue
+
+ internal fun uniffiSetValue(other: UniffiForeignFutureStructU32) {
+ `returnValue` = other.`returnValue`
+ `callStatus` = other.`callStatus`
+ }
+
+}
+internal interface UniffiForeignFutureCompleteU32 : com.sun.jna.Callback {
+ fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU32.UniffiByValue,)
+}
+@Structure.FieldOrder("returnValue", "callStatus")
+internal open class UniffiForeignFutureStructI32(
+ @JvmField internal var `returnValue`: Int = 0,
+ @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+) : Structure() {
+ class UniffiByValue(
+ `returnValue`: Int = 0,
+ `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+ ): UniffiForeignFutureStructI32(`returnValue`,`callStatus`,), Structure.ByValue
+
+ internal fun uniffiSetValue(other: UniffiForeignFutureStructI32) {
+ `returnValue` = other.`returnValue`
+ `callStatus` = other.`callStatus`
+ }
+
+}
+internal interface UniffiForeignFutureCompleteI32 : com.sun.jna.Callback {
+ fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI32.UniffiByValue,)
+}
+@Structure.FieldOrder("returnValue", "callStatus")
+internal open class UniffiForeignFutureStructU64(
+ @JvmField internal var `returnValue`: Long = 0.toLong(),
+ @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+) : Structure() {
+ class UniffiByValue(
+ `returnValue`: Long = 0.toLong(),
+ `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+ ): UniffiForeignFutureStructU64(`returnValue`,`callStatus`,), Structure.ByValue
+
+ internal fun uniffiSetValue(other: UniffiForeignFutureStructU64) {
+ `returnValue` = other.`returnValue`
+ `callStatus` = other.`callStatus`
+ }
+
+}
+internal interface UniffiForeignFutureCompleteU64 : com.sun.jna.Callback {
+ fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU64.UniffiByValue,)
+}
+@Structure.FieldOrder("returnValue", "callStatus")
+internal open class UniffiForeignFutureStructI64(
+ @JvmField internal var `returnValue`: Long = 0.toLong(),
+ @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+) : Structure() {
+ class UniffiByValue(
+ `returnValue`: Long = 0.toLong(),
+ `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+ ): UniffiForeignFutureStructI64(`returnValue`,`callStatus`,), Structure.ByValue
+
+ internal fun uniffiSetValue(other: UniffiForeignFutureStructI64) {
+ `returnValue` = other.`returnValue`
+ `callStatus` = other.`callStatus`
+ }
+
+}
+internal interface UniffiForeignFutureCompleteI64 : com.sun.jna.Callback {
+ fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI64.UniffiByValue,)
+}
+@Structure.FieldOrder("returnValue", "callStatus")
+internal open class UniffiForeignFutureStructF32(
+ @JvmField internal var `returnValue`: Float = 0.0f,
+ @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+) : Structure() {
+ class UniffiByValue(
+ `returnValue`: Float = 0.0f,
+ `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+ ): UniffiForeignFutureStructF32(`returnValue`,`callStatus`,), Structure.ByValue
+
+ internal fun uniffiSetValue(other: UniffiForeignFutureStructF32) {
+ `returnValue` = other.`returnValue`
+ `callStatus` = other.`callStatus`
+ }
+
+}
+internal interface UniffiForeignFutureCompleteF32 : com.sun.jna.Callback {
+ fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructF32.UniffiByValue,)
+}
+@Structure.FieldOrder("returnValue", "callStatus")
+internal open class UniffiForeignFutureStructF64(
+ @JvmField internal var `returnValue`: Double = 0.0,
+ @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+) : Structure() {
+ class UniffiByValue(
+ `returnValue`: Double = 0.0,
+ `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+ ): UniffiForeignFutureStructF64(`returnValue`,`callStatus`,), Structure.ByValue
+
+ internal fun uniffiSetValue(other: UniffiForeignFutureStructF64) {
+ `returnValue` = other.`returnValue`
+ `callStatus` = other.`callStatus`
+ }
+
+}
+internal interface UniffiForeignFutureCompleteF64 : com.sun.jna.Callback {
+ fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructF64.UniffiByValue,)
+}
+@Structure.FieldOrder("returnValue", "callStatus")
+internal open class UniffiForeignFutureStructPointer(
+ @JvmField internal var `returnValue`: Pointer = Pointer.NULL,
+ @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+) : Structure() {
+ class UniffiByValue(
+ `returnValue`: Pointer = Pointer.NULL,
+ `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+ ): UniffiForeignFutureStructPointer(`returnValue`,`callStatus`,), Structure.ByValue
+
+ internal fun uniffiSetValue(other: UniffiForeignFutureStructPointer) {
+ `returnValue` = other.`returnValue`
+ `callStatus` = other.`callStatus`
+ }
+
+}
+internal interface UniffiForeignFutureCompletePointer : com.sun.jna.Callback {
+ fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructPointer.UniffiByValue,)
+}
+@Structure.FieldOrder("returnValue", "callStatus")
+internal open class UniffiForeignFutureStructRustBuffer(
+ @JvmField internal var `returnValue`: RustBuffer.ByValue = RustBuffer.ByValue(),
+ @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+) : Structure() {
+ class UniffiByValue(
+ `returnValue`: RustBuffer.ByValue = RustBuffer.ByValue(),
+ `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+ ): UniffiForeignFutureStructRustBuffer(`returnValue`,`callStatus`,), Structure.ByValue
+
+ internal fun uniffiSetValue(other: UniffiForeignFutureStructRustBuffer) {
+ `returnValue` = other.`returnValue`
+ `callStatus` = other.`callStatus`
+ }
+
+}
+internal interface UniffiForeignFutureCompleteRustBuffer : com.sun.jna.Callback {
+ fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructRustBuffer.UniffiByValue,)
+}
+@Structure.FieldOrder("callStatus")
+internal open class UniffiForeignFutureStructVoid(
+ @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+) : Structure() {
+ class UniffiByValue(
+ `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(),
+ ): UniffiForeignFutureStructVoid(`callStatus`,), Structure.ByValue
+
+ internal fun uniffiSetValue(other: UniffiForeignFutureStructVoid) {
+ `callStatus` = other.`callStatus`
+ }
+
+}
+internal interface UniffiForeignFutureCompleteVoid : com.sun.jna.Callback {
+ fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructVoid.UniffiByValue,)
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// A JNA Library to expose the extern-C FFI definitions.
+// This is an implementation detail which will be called internally by the public API.
+
+internal interface UniffiLib : Library {
+ companion object {
+ internal val INSTANCE: UniffiLib by lazy {
+ loadIndirect(componentName = "noro_mobile_core")
+ .also { lib: UniffiLib ->
+ uniffiCheckContractApiVersion(lib)
+ uniffiCheckApiChecksums(lib)
+ }
+ }
+
+ // The Cleaner for the whole library
+ internal val CLEANER: UniffiCleaner by lazy {
+ UniffiCleaner.create()
+ }
+ }
+
+ fun uniffi_noro_mobile_core_fn_clone_syncclient(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus,
+ ): Pointer
+ fun uniffi_noro_mobile_core_fn_free_syncclient(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus,
+ ): Unit
+ fun uniffi_noro_mobile_core_fn_constructor_syncclient_new(`baseUrl`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): Pointer
+ fun uniffi_noro_mobile_core_fn_method_syncclient_create_item(`ptr`: Pointer,`itemType`: RustBuffer.ByValue,`title`: RustBuffer.ByValue,`data`: RustBuffer.ByValue,`tags`: RustBuffer.ByValue,`favorite`: Byte,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_method_syncclient_delete_item(`ptr`: Pointer,`id`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): Unit
+ fun uniffi_noro_mobile_core_fn_method_syncclient_fetch_items(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_method_syncclient_login(`ptr`: Pointer,`email`: RustBuffer.ByValue,`password`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_method_syncclient_set_token(`ptr`: Pointer,`token`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): Unit
+ fun uniffi_noro_mobile_core_fn_method_syncclient_update_item(`ptr`: Pointer,`id`: RustBuffer.ByValue,`title`: RustBuffer.ByValue,`data`: RustBuffer.ByValue,`tags`: RustBuffer.ByValue,`favorite`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_clone_vault(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus,
+ ): Pointer
+ fun uniffi_noro_mobile_core_fn_free_vault(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus,
+ ): Unit
+ fun uniffi_noro_mobile_core_fn_constructor_vault_new(uniffi_out_err: UniffiRustCallStatus,
+ ): Pointer
+ fun uniffi_noro_mobile_core_fn_method_vault_create_item(`ptr`: Pointer,`itemType`: RustBuffer.ByValue,`title`: RustBuffer.ByValue,`data`: RustBuffer.ByValue,`tags`: RustBuffer.ByValue,`favorite`: Byte,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_method_vault_delete_item(`ptr`: Pointer,`id`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): Unit
+ fun uniffi_noro_mobile_core_fn_method_vault_get_item(`ptr`: Pointer,`id`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_method_vault_list_items(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_method_vault_load(`ptr`: Pointer,`encrypted`: RustBuffer.ByValue,`key`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): Unit
+ fun uniffi_noro_mobile_core_fn_method_vault_save(`ptr`: Pointer,`key`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_method_vault_search_items(`ptr`: Pointer,`query`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_method_vault_update_item(`ptr`: Pointer,`id`: RustBuffer.ByValue,`title`: RustBuffer.ByValue,`data`: RustBuffer.ByValue,`tags`: RustBuffer.ByValue,`favorite`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_func_decrypt(`ciphertext`: RustBuffer.ByValue,`key`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_func_derive_auk(`password`: RustBuffer.ByValue,`secretKey`: RustBuffer.ByValue,`salt`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_func_derive_item_key(`vaultKey`: RustBuffer.ByValue,`itemId`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_func_encrypt(`plaintext`: RustBuffer.ByValue,`key`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_func_generate_item_id(uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_func_generate_salt(uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_func_generate_secret_key(uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_func_generate_vault_key(uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_func_unwrap_vault_key(`wrapped`: RustBuffer.ByValue,`auk`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun uniffi_noro_mobile_core_fn_func_wrap_vault_key(`vaultKey`: RustBuffer.ByValue,`auk`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun ffi_noro_mobile_core_rustbuffer_alloc(`size`: Long,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun ffi_noro_mobile_core_rustbuffer_from_bytes(`bytes`: ForeignBytes.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun ffi_noro_mobile_core_rustbuffer_free(`buf`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
+ ): Unit
+ fun ffi_noro_mobile_core_rustbuffer_reserve(`buf`: RustBuffer.ByValue,`additional`: Long,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun ffi_noro_mobile_core_rust_future_poll_u8(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_cancel_u8(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_free_u8(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_complete_u8(`handle`: Long,uniffi_out_err: UniffiRustCallStatus,
+ ): Byte
+ fun ffi_noro_mobile_core_rust_future_poll_i8(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_cancel_i8(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_free_i8(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_complete_i8(`handle`: Long,uniffi_out_err: UniffiRustCallStatus,
+ ): Byte
+ fun ffi_noro_mobile_core_rust_future_poll_u16(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_cancel_u16(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_free_u16(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_complete_u16(`handle`: Long,uniffi_out_err: UniffiRustCallStatus,
+ ): Short
+ fun ffi_noro_mobile_core_rust_future_poll_i16(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_cancel_i16(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_free_i16(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_complete_i16(`handle`: Long,uniffi_out_err: UniffiRustCallStatus,
+ ): Short
+ fun ffi_noro_mobile_core_rust_future_poll_u32(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_cancel_u32(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_free_u32(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_complete_u32(`handle`: Long,uniffi_out_err: UniffiRustCallStatus,
+ ): Int
+ fun ffi_noro_mobile_core_rust_future_poll_i32(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_cancel_i32(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_free_i32(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_complete_i32(`handle`: Long,uniffi_out_err: UniffiRustCallStatus,
+ ): Int
+ fun ffi_noro_mobile_core_rust_future_poll_u64(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_cancel_u64(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_free_u64(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_complete_u64(`handle`: Long,uniffi_out_err: UniffiRustCallStatus,
+ ): Long
+ fun ffi_noro_mobile_core_rust_future_poll_i64(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_cancel_i64(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_free_i64(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_complete_i64(`handle`: Long,uniffi_out_err: UniffiRustCallStatus,
+ ): Long
+ fun ffi_noro_mobile_core_rust_future_poll_f32(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_cancel_f32(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_free_f32(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_complete_f32(`handle`: Long,uniffi_out_err: UniffiRustCallStatus,
+ ): Float
+ fun ffi_noro_mobile_core_rust_future_poll_f64(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_cancel_f64(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_free_f64(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_complete_f64(`handle`: Long,uniffi_out_err: UniffiRustCallStatus,
+ ): Double
+ fun ffi_noro_mobile_core_rust_future_poll_pointer(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_cancel_pointer(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_free_pointer(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_complete_pointer(`handle`: Long,uniffi_out_err: UniffiRustCallStatus,
+ ): Pointer
+ fun ffi_noro_mobile_core_rust_future_poll_rust_buffer(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_cancel_rust_buffer(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_free_rust_buffer(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_complete_rust_buffer(`handle`: Long,uniffi_out_err: UniffiRustCallStatus,
+ ): RustBuffer.ByValue
+ fun ffi_noro_mobile_core_rust_future_poll_void(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_cancel_void(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_free_void(`handle`: Long,
+ ): Unit
+ fun ffi_noro_mobile_core_rust_future_complete_void(`handle`: Long,uniffi_out_err: UniffiRustCallStatus,
+ ): Unit
+ fun uniffi_noro_mobile_core_checksum_func_decrypt(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_func_derive_auk(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_func_derive_item_key(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_func_encrypt(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_func_generate_item_id(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_func_generate_salt(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_func_generate_secret_key(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_func_generate_vault_key(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_func_unwrap_vault_key(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_func_wrap_vault_key(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_method_syncclient_create_item(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_method_syncclient_delete_item(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_method_syncclient_fetch_items(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_method_syncclient_login(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_method_syncclient_set_token(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_method_syncclient_update_item(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_method_vault_create_item(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_method_vault_delete_item(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_method_vault_get_item(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_method_vault_list_items(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_method_vault_load(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_method_vault_save(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_method_vault_search_items(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_method_vault_update_item(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_constructor_syncclient_new(
+ ): Short
+ fun uniffi_noro_mobile_core_checksum_constructor_vault_new(
+ ): Short
+ fun ffi_noro_mobile_core_uniffi_contract_version(
+ ): Int
+
+}
+
+private fun uniffiCheckContractApiVersion(lib: UniffiLib) {
+ // Get the bindings contract version from our ComponentInterface
+ val bindings_contract_version = 26
+ // Get the scaffolding contract version by calling the into the dylib
+ val scaffolding_contract_version = lib.ffi_noro_mobile_core_uniffi_contract_version()
+ if (bindings_contract_version != scaffolding_contract_version) {
+ throw RuntimeException("UniFFI contract version mismatch: try cleaning and rebuilding your project")
+ }
+}
+
+@Suppress("UNUSED_PARAMETER")
+private fun uniffiCheckApiChecksums(lib: UniffiLib) {
+ if (lib.uniffi_noro_mobile_core_checksum_func_decrypt() != 32839.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_func_derive_auk() != 37186.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_func_derive_item_key() != 51281.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_func_encrypt() != 46133.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_func_generate_item_id() != 22417.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_func_generate_salt() != 31865.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_func_generate_secret_key() != 53137.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_func_generate_vault_key() != 50386.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_func_unwrap_vault_key() != 38585.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_func_wrap_vault_key() != 17122.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_method_syncclient_create_item() != 51847.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_method_syncclient_delete_item() != 22816.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_method_syncclient_fetch_items() != 53777.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_method_syncclient_login() != 19049.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_method_syncclient_set_token() != 22416.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_method_syncclient_update_item() != 45106.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_method_vault_create_item() != 60480.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_method_vault_delete_item() != 63195.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_method_vault_get_item() != 55932.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_method_vault_list_items() != 61447.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_method_vault_load() != 6499.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_method_vault_save() != 50962.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_method_vault_search_items() != 42826.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_method_vault_update_item() != 55533.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_constructor_syncclient_new() != 38165.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_noro_mobile_core_checksum_constructor_vault_new() != 9762.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+}
+
+// Async support
+
+// Public interface members begin here.
+
+
+// Interface implemented by anything that can contain an object reference.
+//
+// Such types expose a `destroy()` method that must be called to cleanly
+// dispose of the contained objects. Failure to call this method may result
+// in memory leaks.
+//
+// The easiest way to ensure this method is called is to use the `.use`
+// helper method to execute a block and destroy the object at the end.
+interface Disposable {
+ fun destroy()
+ companion object {
+ fun destroy(vararg args: Any?) {
+ args.filterIsInstance()
+ .forEach(Disposable::destroy)
+ }
+ }
+}
+
+/**
+ * @suppress
+ */
+inline fun T.use(block: (T) -> R) =
+ try {
+ block(this)
+ } finally {
+ try {
+ // N.B. our implementation is on the nullable type `Disposable?`.
+ this?.destroy()
+ } catch (e: Throwable) {
+ // swallow
+ }
+ }
+
+/**
+ * Used to instantiate an interface without an actual pointer, for fakes in tests, mostly.
+ *
+ * @suppress
+ * */
+object NoPointer
+
+/**
+ * @suppress
+ */
+public object FfiConverterInt: FfiConverter {
+ override fun lift(value: Int): Int {
+ return value
+ }
+
+ override fun read(buf: ByteBuffer): Int {
+ return buf.getInt()
+ }
+
+ override fun lower(value: Int): Int {
+ return value
+ }
+
+ override fun allocationSize(value: Int) = 4UL
+
+ override fun write(value: Int, buf: ByteBuffer) {
+ buf.putInt(value)
+ }
+}
+
+/**
+ * @suppress
+ */
+public object FfiConverterULong: FfiConverter {
+ override fun lift(value: Long): ULong {
+ return value.toULong()
+ }
+
+ override fun read(buf: ByteBuffer): ULong {
+ return lift(buf.getLong())
+ }
+
+ override fun lower(value: ULong): Long {
+ return value.toLong()
+ }
+
+ override fun allocationSize(value: ULong) = 8UL
+
+ override fun write(value: ULong, buf: ByteBuffer) {
+ buf.putLong(value.toLong())
+ }
+}
+
+/**
+ * @suppress
+ */
+public object FfiConverterBoolean: FfiConverter {
+ override fun lift(value: Byte): Boolean {
+ return value.toInt() != 0
+ }
+
+ override fun read(buf: ByteBuffer): Boolean {
+ return lift(buf.get())
+ }
+
+ override fun lower(value: Boolean): Byte {
+ return if (value) 1.toByte() else 0.toByte()
+ }
+
+ override fun allocationSize(value: Boolean) = 1UL
+
+ override fun write(value: Boolean, buf: ByteBuffer) {
+ buf.put(lower(value))
+ }
+}
+
+/**
+ * @suppress
+ */
+public object FfiConverterString: FfiConverter {
+ // Note: we don't inherit from FfiConverterRustBuffer, because we use a
+ // special encoding when lowering/lifting. We can use `RustBuffer.len` to
+ // store our length and avoid writing it out to the buffer.
+ override fun lift(value: RustBuffer.ByValue): String {
+ try {
+ val byteArr = ByteArray(value.len.toInt())
+ value.asByteBuffer()!!.get(byteArr)
+ return byteArr.toString(Charsets.UTF_8)
+ } finally {
+ RustBuffer.free(value)
+ }
+ }
+
+ override fun read(buf: ByteBuffer): String {
+ val len = buf.getInt()
+ val byteArr = ByteArray(len)
+ buf.get(byteArr)
+ return byteArr.toString(Charsets.UTF_8)
+ }
+
+ fun toUtf8(value: String): ByteBuffer {
+ // Make sure we don't have invalid UTF-16, check for lone surrogates.
+ return Charsets.UTF_8.newEncoder().run {
+ onMalformedInput(CodingErrorAction.REPORT)
+ encode(CharBuffer.wrap(value))
+ }
+ }
+
+ override fun lower(value: String): RustBuffer.ByValue {
+ val byteBuf = toUtf8(value)
+ // Ideally we'd pass these bytes to `ffi_bytebuffer_from_bytes`, but doing so would require us
+ // to copy them into a JNA `Memory`. So we might as well directly copy them into a `RustBuffer`.
+ val rbuf = RustBuffer.alloc(byteBuf.limit().toULong())
+ rbuf.asByteBuffer()!!.put(byteBuf)
+ return rbuf
+ }
+
+ // We aren't sure exactly how many bytes our string will be once it's UTF-8
+ // encoded. Allocate 3 bytes per UTF-16 code unit which will always be
+ // enough.
+ override fun allocationSize(value: String): ULong {
+ val sizeForLength = 4UL
+ val sizeForString = value.length.toULong() * 3UL
+ return sizeForLength + sizeForString
+ }
+
+ override fun write(value: String, buf: ByteBuffer) {
+ val byteBuf = toUtf8(value)
+ buf.putInt(byteBuf.limit())
+ buf.put(byteBuf)
+ }
+}
+
+/**
+ * @suppress
+ */
+public object FfiConverterByteArray: FfiConverterRustBuffer {
+ override fun read(buf: ByteBuffer): ByteArray {
+ val len = buf.getInt()
+ val byteArr = ByteArray(len)
+ buf.get(byteArr)
+ return byteArr
+ }
+ override fun allocationSize(value: ByteArray): ULong {
+ return 4UL + value.size.toULong()
+ }
+ override fun write(value: ByteArray, buf: ByteBuffer) {
+ buf.putInt(value.size)
+ buf.put(value)
+ }
+}
+
+
+// This template implements a class for working with a Rust struct via a Pointer/Arc
+// to the live Rust struct on the other side of the FFI.
+//
+// Each instance implements core operations for working with the Rust `Arc` and the
+// Kotlin Pointer to work with the live Rust struct on the other side of the FFI.
+//
+// There's some subtlety here, because we have to be careful not to operate on a Rust
+// struct after it has been dropped, and because we must expose a public API for freeing
+// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are:
+//
+// * Each instance holds an opaque pointer to the underlying Rust struct.
+// Method calls need to read this pointer from the object's state and pass it in to
+// the Rust FFI.
+//
+// * When an instance is no longer needed, its pointer should be passed to a
+// special destructor function provided by the Rust FFI, which will drop the
+// underlying Rust struct.
+//
+// * Given an instance, calling code is expected to call the special
+// `destroy` method in order to free it after use, either by calling it explicitly
+// or by using a higher-level helper like the `use` method. Failing to do so risks
+// leaking the underlying Rust struct.
+//
+// * We can't assume that calling code will do the right thing, and must be prepared
+// to handle Kotlin method calls executing concurrently with or even after a call to
+// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`.
+//
+// * We must never allow Rust code to operate on the underlying Rust struct after
+// the destructor has been called, and must never call the destructor more than once.
+// Doing so may trigger memory unsafety.
+//
+// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner`
+// is implemented to call the destructor when the Kotlin object becomes unreachable.
+// This is done in a background thread. This is not a panacea, and client code should be aware that
+// 1. the thread may starve if some there are objects that have poorly performing
+// `drop` methods or do significant work in their `drop` methods.
+// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`,
+// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html).
+//
+// If we try to implement this with mutual exclusion on access to the pointer, there is the
+// possibility of a race between a method call and a concurrent call to `destroy`:
+//
+// * Thread A starts a method call, reads the value of the pointer, but is interrupted
+// before it can pass the pointer over the FFI to Rust.
+// * Thread B calls `destroy` and frees the underlying Rust struct.
+// * Thread A resumes, passing the already-read pointer value to Rust and triggering
+// a use-after-free.
+//
+// One possible solution would be to use a `ReadWriteLock`, with each method call taking
+// a read lock (and thus allowed to run concurrently) and the special `destroy` method
+// taking a write lock (and thus blocking on live method calls). However, we aim not to
+// generate methods with any hidden blocking semantics, and a `destroy` method that might
+// block if called incorrectly seems to meet that bar.
+//
+// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track
+// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy`
+// has been called. These are updated according to the following rules:
+//
+// * The initial value of the counter is 1, indicating a live object with no in-flight calls.
+// The initial value for the flag is false.
+//
+// * At the start of each method call, we atomically check the counter.
+// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted.
+// If it is nonzero them we atomically increment it by 1 and proceed with the method call.
+//
+// * At the end of each method call, we atomically decrement and check the counter.
+// If it has reached zero then we destroy the underlying Rust struct.
+//
+// * When `destroy` is called, we atomically flip the flag from false to true.
+// If the flag was already true we silently fail.
+// Otherwise we atomically decrement and check the counter.
+// If it has reached zero then we destroy the underlying Rust struct.
+//
+// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works,
+// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`.
+//
+// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been
+// called *and* all in-flight method calls have completed, avoiding violating any of the expectations
+// of the underlying Rust code.
+//
+// This makes a cleaner a better alternative to _not_ calling `destroy()` as
+// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop`
+// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner
+// thread may be starved, and the app will leak memory.
+//
+// In this case, `destroy`ing manually may be a better solution.
+//
+// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects
+// with Rust peers are reclaimed:
+//
+// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen:
+// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then:
+// 3. The memory is reclaimed when the process terminates.
+//
+// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219
+//
+
+
+/**
+ * The cleaner interface for Object finalization code to run.
+ * This is the entry point to any implementation that we're using.
+ *
+ * The cleaner registers objects and returns cleanables, so now we are
+ * defining a `UniffiCleaner` with a `UniffiClenaer.Cleanable` to abstract the
+ * different implmentations available at compile time.
+ *
+ * @suppress
+ */
+interface UniffiCleaner {
+ interface Cleanable {
+ fun clean()
+ }
+
+ fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable
+
+ companion object
+}
+
+// The fallback Jna cleaner, which is available for both Android, and the JVM.
+private class UniffiJnaCleaner : UniffiCleaner {
+ private val cleaner = com.sun.jna.internal.Cleaner.getCleaner()
+
+ override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable =
+ UniffiJnaCleanable(cleaner.register(value, cleanUpTask))
+}
+
+private class UniffiJnaCleanable(
+ private val cleanable: com.sun.jna.internal.Cleaner.Cleanable,
+) : UniffiCleaner.Cleanable {
+ override fun clean() = cleanable.clean()
+}
+
+// We decide at uniffi binding generation time whether we were
+// using Android or not.
+// There are further runtime checks to chose the correct implementation
+// of the cleaner.
+private fun UniffiCleaner.Companion.create(): UniffiCleaner =
+ try {
+ // For safety's sake: if the library hasn't been run in android_cleaner = true
+ // mode, but is being run on Android, then we still need to think about
+ // Android API versions.
+ // So we check if java.lang.ref.Cleaner is there, and use that…
+ java.lang.Class.forName("java.lang.ref.Cleaner")
+ JavaLangRefCleaner()
+ } catch (e: ClassNotFoundException) {
+ // … otherwise, fallback to the JNA cleaner.
+ UniffiJnaCleaner()
+ }
+
+private class JavaLangRefCleaner : UniffiCleaner {
+ val cleaner = java.lang.ref.Cleaner.create()
+
+ override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable =
+ JavaLangRefCleanable(cleaner.register(value, cleanUpTask))
+}
+
+private class JavaLangRefCleanable(
+ val cleanable: java.lang.ref.Cleaner.Cleanable
+) : UniffiCleaner.Cleanable {
+ override fun clean() = cleanable.clean()
+}
+public interface SyncClientInterface {
+
+ fun `createItem`(`itemType`: kotlin.String, `title`: kotlin.String, `data`: kotlin.ByteArray, `tags`: List, `favorite`: kotlin.Boolean): VaultItem
+
+ fun `deleteItem`(`id`: kotlin.String)
+
+ fun `fetchItems`(): List
+
+ fun `login`(`email`: kotlin.String, `password`: kotlin.String): kotlin.String
+
+ fun `setToken`(`token`: kotlin.String)
+
+ fun `updateItem`(`id`: kotlin.String, `title`: kotlin.String?, `data`: kotlin.ByteArray?, `tags`: List?, `favorite`: kotlin.Boolean?): VaultItem
+
+ companion object
+}
+
+open class SyncClient: Disposable, AutoCloseable, SyncClientInterface {
+
+ constructor(pointer: Pointer) {
+ this.pointer = pointer
+ this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer))
+ }
+
+ /**
+ * This constructor can be used to instantiate a fake object. Only used for tests. Any
+ * attempt to actually use an object constructed this way will fail as there is no
+ * connected Rust object.
+ */
+ @Suppress("UNUSED_PARAMETER")
+ constructor(noPointer: NoPointer) {
+ this.pointer = null
+ this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer))
+ }
+ constructor(`baseUrl`: kotlin.String) :
+ this(
+ uniffiRustCall() { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_constructor_syncclient_new(
+ FfiConverterString.lower(`baseUrl`),_status)
+}
+ )
+
+ protected val pointer: Pointer?
+ protected val cleanable: UniffiCleaner.Cleanable
+
+ private val wasDestroyed = AtomicBoolean(false)
+ private val callCounter = AtomicLong(1)
+
+ override fun destroy() {
+ // Only allow a single call to this method.
+ // TODO: maybe we should log a warning if called more than once?
+ if (this.wasDestroyed.compareAndSet(false, true)) {
+ // This decrement always matches the initial count of 1 given at creation time.
+ if (this.callCounter.decrementAndGet() == 0L) {
+ cleanable.clean()
+ }
+ }
+ }
+
+ @Synchronized
+ override fun close() {
+ this.destroy()
+ }
+
+ internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R {
+ // Check and increment the call counter, to keep the object alive.
+ // This needs a compare-and-set retry loop in case of concurrent updates.
+ do {
+ val c = this.callCounter.get()
+ if (c == 0L) {
+ throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed")
+ }
+ if (c == Long.MAX_VALUE) {
+ throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow")
+ }
+ } while (! this.callCounter.compareAndSet(c, c + 1L))
+ // Now we can safely do the method call without the pointer being freed concurrently.
+ try {
+ return block(this.uniffiClonePointer())
+ } finally {
+ // This decrement always matches the increment we performed above.
+ if (this.callCounter.decrementAndGet() == 0L) {
+ cleanable.clean()
+ }
+ }
+ }
+
+ // Use a static inner class instead of a closure so as not to accidentally
+ // capture `this` as part of the cleanable's action.
+ private class UniffiCleanAction(private val pointer: Pointer?) : Runnable {
+ override fun run() {
+ pointer?.let { ptr ->
+ uniffiRustCall { status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_free_syncclient(ptr, status)
+ }
+ }
+ }
+ }
+
+ fun uniffiClonePointer(): Pointer {
+ return uniffiRustCall() { status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_clone_syncclient(pointer!!, status)
+ }
+ }
+
+
+ @Throws(SyncException::class)override fun `createItem`(`itemType`: kotlin.String, `title`: kotlin.String, `data`: kotlin.ByteArray, `tags`: List, `favorite`: kotlin.Boolean): VaultItem {
+ return FfiConverterTypeVaultItem.lift(
+ callWithPointer {
+ uniffiRustCallWithError(SyncException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_method_syncclient_create_item(
+ it, FfiConverterString.lower(`itemType`),FfiConverterString.lower(`title`),FfiConverterByteArray.lower(`data`),FfiConverterSequenceString.lower(`tags`),FfiConverterBoolean.lower(`favorite`),_status)
+}
+ }
+ )
+ }
+
+
+
+ @Throws(SyncException::class)override fun `deleteItem`(`id`: kotlin.String)
+ =
+ callWithPointer {
+ uniffiRustCallWithError(SyncException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_method_syncclient_delete_item(
+ it, FfiConverterString.lower(`id`),_status)
+}
+ }
+
+
+
+
+ @Throws(SyncException::class)override fun `fetchItems`(): List {
+ return FfiConverterSequenceTypeVaultItem.lift(
+ callWithPointer {
+ uniffiRustCallWithError(SyncException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_method_syncclient_fetch_items(
+ it, _status)
+}
+ }
+ )
+ }
+
+
+
+ @Throws(SyncException::class)override fun `login`(`email`: kotlin.String, `password`: kotlin.String): kotlin.String {
+ return FfiConverterString.lift(
+ callWithPointer {
+ uniffiRustCallWithError(SyncException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_method_syncclient_login(
+ it, FfiConverterString.lower(`email`),FfiConverterString.lower(`password`),_status)
+}
+ }
+ )
+ }
+
+
+ override fun `setToken`(`token`: kotlin.String)
+ =
+ callWithPointer {
+ uniffiRustCall() { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_method_syncclient_set_token(
+ it, FfiConverterString.lower(`token`),_status)
+}
+ }
+
+
+
+
+ @Throws(SyncException::class)override fun `updateItem`(`id`: kotlin.String, `title`: kotlin.String?, `data`: kotlin.ByteArray?, `tags`: List?, `favorite`: kotlin.Boolean?): VaultItem {
+ return FfiConverterTypeVaultItem.lift(
+ callWithPointer {
+ uniffiRustCallWithError(SyncException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_method_syncclient_update_item(
+ it, FfiConverterString.lower(`id`),FfiConverterOptionalString.lower(`title`),FfiConverterOptionalByteArray.lower(`data`),FfiConverterOptionalSequenceString.lower(`tags`),FfiConverterOptionalBoolean.lower(`favorite`),_status)
+}
+ }
+ )
+ }
+
+
+
+
+
+
+ companion object
+
+}
+
+/**
+ * @suppress
+ */
+public object FfiConverterTypeSyncClient: FfiConverter {
+
+ override fun lower(value: SyncClient): Pointer {
+ return value.uniffiClonePointer()
+ }
+
+ override fun lift(value: Pointer): SyncClient {
+ return SyncClient(value)
+ }
+
+ override fun read(buf: ByteBuffer): SyncClient {
+ // The Rust code always writes pointers as 8 bytes, and will
+ // fail to compile if they don't fit.
+ return lift(Pointer(buf.getLong()))
+ }
+
+ override fun allocationSize(value: SyncClient) = 8UL
+
+ override fun write(value: SyncClient, buf: ByteBuffer) {
+ // The Rust code always expects pointers written as 8 bytes,
+ // and will fail to compile if they don't fit.
+ buf.putLong(Pointer.nativeValue(lower(value)))
+ }
+}
+
+
+// This template implements a class for working with a Rust struct via a Pointer/Arc
+// to the live Rust struct on the other side of the FFI.
+//
+// Each instance implements core operations for working with the Rust `Arc` and the
+// Kotlin Pointer to work with the live Rust struct on the other side of the FFI.
+//
+// There's some subtlety here, because we have to be careful not to operate on a Rust
+// struct after it has been dropped, and because we must expose a public API for freeing
+// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are:
+//
+// * Each instance holds an opaque pointer to the underlying Rust struct.
+// Method calls need to read this pointer from the object's state and pass it in to
+// the Rust FFI.
+//
+// * When an instance is no longer needed, its pointer should be passed to a
+// special destructor function provided by the Rust FFI, which will drop the
+// underlying Rust struct.
+//
+// * Given an instance, calling code is expected to call the special
+// `destroy` method in order to free it after use, either by calling it explicitly
+// or by using a higher-level helper like the `use` method. Failing to do so risks
+// leaking the underlying Rust struct.
+//
+// * We can't assume that calling code will do the right thing, and must be prepared
+// to handle Kotlin method calls executing concurrently with or even after a call to
+// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`.
+//
+// * We must never allow Rust code to operate on the underlying Rust struct after
+// the destructor has been called, and must never call the destructor more than once.
+// Doing so may trigger memory unsafety.
+//
+// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner`
+// is implemented to call the destructor when the Kotlin object becomes unreachable.
+// This is done in a background thread. This is not a panacea, and client code should be aware that
+// 1. the thread may starve if some there are objects that have poorly performing
+// `drop` methods or do significant work in their `drop` methods.
+// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`,
+// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html).
+//
+// If we try to implement this with mutual exclusion on access to the pointer, there is the
+// possibility of a race between a method call and a concurrent call to `destroy`:
+//
+// * Thread A starts a method call, reads the value of the pointer, but is interrupted
+// before it can pass the pointer over the FFI to Rust.
+// * Thread B calls `destroy` and frees the underlying Rust struct.
+// * Thread A resumes, passing the already-read pointer value to Rust and triggering
+// a use-after-free.
+//
+// One possible solution would be to use a `ReadWriteLock`, with each method call taking
+// a read lock (and thus allowed to run concurrently) and the special `destroy` method
+// taking a write lock (and thus blocking on live method calls). However, we aim not to
+// generate methods with any hidden blocking semantics, and a `destroy` method that might
+// block if called incorrectly seems to meet that bar.
+//
+// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track
+// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy`
+// has been called. These are updated according to the following rules:
+//
+// * The initial value of the counter is 1, indicating a live object with no in-flight calls.
+// The initial value for the flag is false.
+//
+// * At the start of each method call, we atomically check the counter.
+// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted.
+// If it is nonzero them we atomically increment it by 1 and proceed with the method call.
+//
+// * At the end of each method call, we atomically decrement and check the counter.
+// If it has reached zero then we destroy the underlying Rust struct.
+//
+// * When `destroy` is called, we atomically flip the flag from false to true.
+// If the flag was already true we silently fail.
+// Otherwise we atomically decrement and check the counter.
+// If it has reached zero then we destroy the underlying Rust struct.
+//
+// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works,
+// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`.
+//
+// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been
+// called *and* all in-flight method calls have completed, avoiding violating any of the expectations
+// of the underlying Rust code.
+//
+// This makes a cleaner a better alternative to _not_ calling `destroy()` as
+// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop`
+// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner
+// thread may be starved, and the app will leak memory.
+//
+// In this case, `destroy`ing manually may be a better solution.
+//
+// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects
+// with Rust peers are reclaimed:
+//
+// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen:
+// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then:
+// 3. The memory is reclaimed when the process terminates.
+//
+// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219
+//
+
+
+public interface VaultInterface {
+
+ fun `createItem`(`itemType`: kotlin.String, `title`: kotlin.String, `data`: kotlin.ByteArray, `tags`: List, `favorite`: kotlin.Boolean): VaultItem
+
+ fun `deleteItem`(`id`: kotlin.String)
+
+ fun `getItem`(`id`: kotlin.String): VaultItem?
+
+ fun `listItems`(): List
+
+ fun `load`(`encrypted`: kotlin.ByteArray, `key`: kotlin.ByteArray)
+
+ fun `save`(`key`: kotlin.ByteArray): kotlin.ByteArray
+
+ fun `searchItems`(`query`: kotlin.String): List
+
+ fun `updateItem`(`id`: kotlin.String, `title`: kotlin.String?, `data`: kotlin.ByteArray?, `tags`: List?, `favorite`: kotlin.Boolean?): VaultItem
+
+ companion object
+}
+
+open class Vault: Disposable, AutoCloseable, VaultInterface {
+
+ constructor(pointer: Pointer) {
+ this.pointer = pointer
+ this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer))
+ }
+
+ /**
+ * This constructor can be used to instantiate a fake object. Only used for tests. Any
+ * attempt to actually use an object constructed this way will fail as there is no
+ * connected Rust object.
+ */
+ @Suppress("UNUSED_PARAMETER")
+ constructor(noPointer: NoPointer) {
+ this.pointer = null
+ this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer))
+ }
+ constructor() :
+ this(
+ uniffiRustCall() { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_constructor_vault_new(
+ _status)
+}
+ )
+
+ protected val pointer: Pointer?
+ protected val cleanable: UniffiCleaner.Cleanable
+
+ private val wasDestroyed = AtomicBoolean(false)
+ private val callCounter = AtomicLong(1)
+
+ override fun destroy() {
+ // Only allow a single call to this method.
+ // TODO: maybe we should log a warning if called more than once?
+ if (this.wasDestroyed.compareAndSet(false, true)) {
+ // This decrement always matches the initial count of 1 given at creation time.
+ if (this.callCounter.decrementAndGet() == 0L) {
+ cleanable.clean()
+ }
+ }
+ }
+
+ @Synchronized
+ override fun close() {
+ this.destroy()
+ }
+
+ internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R {
+ // Check and increment the call counter, to keep the object alive.
+ // This needs a compare-and-set retry loop in case of concurrent updates.
+ do {
+ val c = this.callCounter.get()
+ if (c == 0L) {
+ throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed")
+ }
+ if (c == Long.MAX_VALUE) {
+ throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow")
+ }
+ } while (! this.callCounter.compareAndSet(c, c + 1L))
+ // Now we can safely do the method call without the pointer being freed concurrently.
+ try {
+ return block(this.uniffiClonePointer())
+ } finally {
+ // This decrement always matches the increment we performed above.
+ if (this.callCounter.decrementAndGet() == 0L) {
+ cleanable.clean()
+ }
+ }
+ }
+
+ // Use a static inner class instead of a closure so as not to accidentally
+ // capture `this` as part of the cleanable's action.
+ private class UniffiCleanAction(private val pointer: Pointer?) : Runnable {
+ override fun run() {
+ pointer?.let { ptr ->
+ uniffiRustCall { status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_free_vault(ptr, status)
+ }
+ }
+ }
+ }
+
+ fun uniffiClonePointer(): Pointer {
+ return uniffiRustCall() { status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_clone_vault(pointer!!, status)
+ }
+ }
+
+
+ @Throws(VaultException::class)override fun `createItem`(`itemType`: kotlin.String, `title`: kotlin.String, `data`: kotlin.ByteArray, `tags`: List, `favorite`: kotlin.Boolean): VaultItem {
+ return FfiConverterTypeVaultItem.lift(
+ callWithPointer {
+ uniffiRustCallWithError(VaultException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_method_vault_create_item(
+ it, FfiConverterString.lower(`itemType`),FfiConverterString.lower(`title`),FfiConverterByteArray.lower(`data`),FfiConverterSequenceString.lower(`tags`),FfiConverterBoolean.lower(`favorite`),_status)
+}
+ }
+ )
+ }
+
+
+
+ @Throws(VaultException::class)override fun `deleteItem`(`id`: kotlin.String)
+ =
+ callWithPointer {
+ uniffiRustCallWithError(VaultException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_method_vault_delete_item(
+ it, FfiConverterString.lower(`id`),_status)
+}
+ }
+
+
+
+
+ @Throws(VaultException::class)override fun `getItem`(`id`: kotlin.String): VaultItem? {
+ return FfiConverterOptionalTypeVaultItem.lift(
+ callWithPointer {
+ uniffiRustCallWithError(VaultException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_method_vault_get_item(
+ it, FfiConverterString.lower(`id`),_status)
+}
+ }
+ )
+ }
+
+
+ override fun `listItems`(): List {
+ return FfiConverterSequenceTypeVaultItem.lift(
+ callWithPointer {
+ uniffiRustCall() { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_method_vault_list_items(
+ it, _status)
+}
+ }
+ )
+ }
+
+
+
+ @Throws(VaultException::class)override fun `load`(`encrypted`: kotlin.ByteArray, `key`: kotlin.ByteArray)
+ =
+ callWithPointer {
+ uniffiRustCallWithError(VaultException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_method_vault_load(
+ it, FfiConverterByteArray.lower(`encrypted`),FfiConverterByteArray.lower(`key`),_status)
+}
+ }
+
+
+
+
+ @Throws(VaultException::class)override fun `save`(`key`: kotlin.ByteArray): kotlin.ByteArray {
+ return FfiConverterByteArray.lift(
+ callWithPointer {
+ uniffiRustCallWithError(VaultException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_method_vault_save(
+ it, FfiConverterByteArray.lower(`key`),_status)
+}
+ }
+ )
+ }
+
+
+ override fun `searchItems`(`query`: kotlin.String): List {
+ return FfiConverterSequenceTypeVaultItem.lift(
+ callWithPointer {
+ uniffiRustCall() { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_method_vault_search_items(
+ it, FfiConverterString.lower(`query`),_status)
+}
+ }
+ )
+ }
+
+
+
+ @Throws(VaultException::class)override fun `updateItem`(`id`: kotlin.String, `title`: kotlin.String?, `data`: kotlin.ByteArray?, `tags`: List?, `favorite`: kotlin.Boolean?): VaultItem {
+ return FfiConverterTypeVaultItem.lift(
+ callWithPointer {
+ uniffiRustCallWithError(VaultException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_method_vault_update_item(
+ it, FfiConverterString.lower(`id`),FfiConverterOptionalString.lower(`title`),FfiConverterOptionalByteArray.lower(`data`),FfiConverterOptionalSequenceString.lower(`tags`),FfiConverterOptionalBoolean.lower(`favorite`),_status)
+}
+ }
+ )
+ }
+
+
+
+
+
+
+ companion object
+
+}
+
+/**
+ * @suppress
+ */
+public object FfiConverterTypeVault: FfiConverter {
+
+ override fun lower(value: Vault): Pointer {
+ return value.uniffiClonePointer()
+ }
+
+ override fun lift(value: Pointer): Vault {
+ return Vault(value)
+ }
+
+ override fun read(buf: ByteBuffer): Vault {
+ // The Rust code always writes pointers as 8 bytes, and will
+ // fail to compile if they don't fit.
+ return lift(Pointer(buf.getLong()))
+ }
+
+ override fun allocationSize(value: Vault) = 8UL
+
+ override fun write(value: Vault, buf: ByteBuffer) {
+ // The Rust code always expects pointers written as 8 bytes,
+ // and will fail to compile if they don't fit.
+ buf.putLong(Pointer.nativeValue(lower(value)))
+ }
+}
+
+
+
+data class VaultData (
+ var `items`: List,
+ var `updated`: kotlin.ULong
+) {
+
+ companion object
+}
+
+/**
+ * @suppress
+ */
+public object FfiConverterTypeVaultData: FfiConverterRustBuffer {
+ override fun read(buf: ByteBuffer): VaultData {
+ return VaultData(
+ FfiConverterSequenceTypeVaultItem.read(buf),
+ FfiConverterULong.read(buf),
+ )
+ }
+
+ override fun allocationSize(value: VaultData) = (
+ FfiConverterSequenceTypeVaultItem.allocationSize(value.`items`) +
+ FfiConverterULong.allocationSize(value.`updated`)
+ )
+
+ override fun write(value: VaultData, buf: ByteBuffer) {
+ FfiConverterSequenceTypeVaultItem.write(value.`items`, buf)
+ FfiConverterULong.write(value.`updated`, buf)
+ }
+}
+
+
+
+data class VaultItem (
+ var `id`: kotlin.String,
+ var `itemType`: kotlin.String,
+ var `title`: kotlin.String,
+ var `data`: kotlin.ByteArray,
+ var `revision`: kotlin.Int,
+ var `favorite`: kotlin.Boolean,
+ var `deleted`: kotlin.Boolean,
+ var `tags`: List,
+ var `created`: kotlin.ULong,
+ var `updated`: kotlin.ULong
+) {
+
+ companion object
+}
+
+/**
+ * @suppress
+ */
+public object FfiConverterTypeVaultItem: FfiConverterRustBuffer {
+ override fun read(buf: ByteBuffer): VaultItem {
+ return VaultItem(
+ FfiConverterString.read(buf),
+ FfiConverterString.read(buf),
+ FfiConverterString.read(buf),
+ FfiConverterByteArray.read(buf),
+ FfiConverterInt.read(buf),
+ FfiConverterBoolean.read(buf),
+ FfiConverterBoolean.read(buf),
+ FfiConverterSequenceString.read(buf),
+ FfiConverterULong.read(buf),
+ FfiConverterULong.read(buf),
+ )
+ }
+
+ override fun allocationSize(value: VaultItem) = (
+ FfiConverterString.allocationSize(value.`id`) +
+ FfiConverterString.allocationSize(value.`itemType`) +
+ FfiConverterString.allocationSize(value.`title`) +
+ FfiConverterByteArray.allocationSize(value.`data`) +
+ FfiConverterInt.allocationSize(value.`revision`) +
+ FfiConverterBoolean.allocationSize(value.`favorite`) +
+ FfiConverterBoolean.allocationSize(value.`deleted`) +
+ FfiConverterSequenceString.allocationSize(value.`tags`) +
+ FfiConverterULong.allocationSize(value.`created`) +
+ FfiConverterULong.allocationSize(value.`updated`)
+ )
+
+ override fun write(value: VaultItem, buf: ByteBuffer) {
+ FfiConverterString.write(value.`id`, buf)
+ FfiConverterString.write(value.`itemType`, buf)
+ FfiConverterString.write(value.`title`, buf)
+ FfiConverterByteArray.write(value.`data`, buf)
+ FfiConverterInt.write(value.`revision`, buf)
+ FfiConverterBoolean.write(value.`favorite`, buf)
+ FfiConverterBoolean.write(value.`deleted`, buf)
+ FfiConverterSequenceString.write(value.`tags`, buf)
+ FfiConverterULong.write(value.`created`, buf)
+ FfiConverterULong.write(value.`updated`, buf)
+ }
+}
+
+
+
+
+
+sealed class CryptoException: kotlin.Exception() {
+
+ class Argon2(
+ ) : CryptoException() {
+ override val message
+ get() = ""
+ }
+
+ class Encryption(
+ ) : CryptoException() {
+ override val message
+ get() = ""
+ }
+
+ class Decryption(
+ ) : CryptoException() {
+ override val message
+ get() = ""
+ }
+
+ class InvalidSecretKey(
+ ) : CryptoException() {
+ override val message
+ get() = ""
+ }
+
+ class InvalidKeyLength(
+ ) : CryptoException() {
+ override val message
+ get() = ""
+ }
+
+
+ companion object ErrorHandler : UniffiRustCallStatusErrorHandler {
+ override fun lift(error_buf: RustBuffer.ByValue): CryptoException = FfiConverterTypeCryptoError.lift(error_buf)
+ }
+
+
+}
+
+/**
+ * @suppress
+ */
+public object FfiConverterTypeCryptoError : FfiConverterRustBuffer {
+ override fun read(buf: ByteBuffer): CryptoException {
+
+
+ return when(buf.getInt()) {
+ 1 -> CryptoException.Argon2()
+ 2 -> CryptoException.Encryption()
+ 3 -> CryptoException.Decryption()
+ 4 -> CryptoException.InvalidSecretKey()
+ 5 -> CryptoException.InvalidKeyLength()
+ else -> throw RuntimeException("invalid error enum value, something is very wrong!!")
+ }
+ }
+
+ override fun allocationSize(value: CryptoException): ULong {
+ return when(value) {
+ is CryptoException.Argon2 -> (
+ // Add the size for the Int that specifies the variant plus the size needed for all fields
+ 4UL
+ )
+ is CryptoException.Encryption -> (
+ // Add the size for the Int that specifies the variant plus the size needed for all fields
+ 4UL
+ )
+ is CryptoException.Decryption -> (
+ // Add the size for the Int that specifies the variant plus the size needed for all fields
+ 4UL
+ )
+ is CryptoException.InvalidSecretKey -> (
+ // Add the size for the Int that specifies the variant plus the size needed for all fields
+ 4UL
+ )
+ is CryptoException.InvalidKeyLength -> (
+ // Add the size for the Int that specifies the variant plus the size needed for all fields
+ 4UL
+ )
+ }
+ }
+
+ override fun write(value: CryptoException, buf: ByteBuffer) {
+ when(value) {
+ is CryptoException.Argon2 -> {
+ buf.putInt(1)
+ Unit
+ }
+ is CryptoException.Encryption -> {
+ buf.putInt(2)
+ Unit
+ }
+ is CryptoException.Decryption -> {
+ buf.putInt(3)
+ Unit
+ }
+ is CryptoException.InvalidSecretKey -> {
+ buf.putInt(4)
+ Unit
+ }
+ is CryptoException.InvalidKeyLength -> {
+ buf.putInt(5)
+ Unit
+ }
+ }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ }
+ }
+
+}
+
+
+
+
+
+sealed class SyncException: kotlin.Exception() {
+
+ class Http(
+ ) : SyncException() {
+ override val message
+ get() = ""
+ }
+
+ class Auth(
+ ) : SyncException() {
+ override val message
+ get() = ""
+ }
+
+ class Conflict(
+ ) : SyncException() {
+ override val message
+ get() = ""
+ }
+
+ class Parse(
+ ) : SyncException() {
+ override val message
+ get() = ""
+ }
+
+
+ companion object ErrorHandler : UniffiRustCallStatusErrorHandler {
+ override fun lift(error_buf: RustBuffer.ByValue): SyncException = FfiConverterTypeSyncError.lift(error_buf)
+ }
+
+
+}
+
+/**
+ * @suppress
+ */
+public object FfiConverterTypeSyncError : FfiConverterRustBuffer {
+ override fun read(buf: ByteBuffer): SyncException {
+
+
+ return when(buf.getInt()) {
+ 1 -> SyncException.Http()
+ 2 -> SyncException.Auth()
+ 3 -> SyncException.Conflict()
+ 4 -> SyncException.Parse()
+ else -> throw RuntimeException("invalid error enum value, something is very wrong!!")
+ }
+ }
+
+ override fun allocationSize(value: SyncException): ULong {
+ return when(value) {
+ is SyncException.Http -> (
+ // Add the size for the Int that specifies the variant plus the size needed for all fields
+ 4UL
+ )
+ is SyncException.Auth -> (
+ // Add the size for the Int that specifies the variant plus the size needed for all fields
+ 4UL
+ )
+ is SyncException.Conflict -> (
+ // Add the size for the Int that specifies the variant plus the size needed for all fields
+ 4UL
+ )
+ is SyncException.Parse -> (
+ // Add the size for the Int that specifies the variant plus the size needed for all fields
+ 4UL
+ )
+ }
+ }
+
+ override fun write(value: SyncException, buf: ByteBuffer) {
+ when(value) {
+ is SyncException.Http -> {
+ buf.putInt(1)
+ Unit
+ }
+ is SyncException.Auth -> {
+ buf.putInt(2)
+ Unit
+ }
+ is SyncException.Conflict -> {
+ buf.putInt(3)
+ Unit
+ }
+ is SyncException.Parse -> {
+ buf.putInt(4)
+ Unit
+ }
+ }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ }
+ }
+
+}
+
+
+
+
+
+sealed class VaultException: kotlin.Exception() {
+
+ class NotFound(
+ ) : VaultException() {
+ override val message
+ get() = ""
+ }
+
+ class Serialization(
+ ) : VaultException() {
+ override val message
+ get() = ""
+ }
+
+ class Crypto(
+ ) : VaultException() {
+ override val message
+ get() = ""
+ }
+
+
+ companion object ErrorHandler : UniffiRustCallStatusErrorHandler {
+ override fun lift(error_buf: RustBuffer.ByValue): VaultException = FfiConverterTypeVaultError.lift(error_buf)
+ }
+
+
+}
+
+/**
+ * @suppress
+ */
+public object FfiConverterTypeVaultError : FfiConverterRustBuffer {
+ override fun read(buf: ByteBuffer): VaultException {
+
+
+ return when(buf.getInt()) {
+ 1 -> VaultException.NotFound()
+ 2 -> VaultException.Serialization()
+ 3 -> VaultException.Crypto()
+ else -> throw RuntimeException("invalid error enum value, something is very wrong!!")
+ }
+ }
+
+ override fun allocationSize(value: VaultException): ULong {
+ return when(value) {
+ is VaultException.NotFound -> (
+ // Add the size for the Int that specifies the variant plus the size needed for all fields
+ 4UL
+ )
+ is VaultException.Serialization -> (
+ // Add the size for the Int that specifies the variant plus the size needed for all fields
+ 4UL
+ )
+ is VaultException.Crypto -> (
+ // Add the size for the Int that specifies the variant plus the size needed for all fields
+ 4UL
+ )
+ }
+ }
+
+ override fun write(value: VaultException, buf: ByteBuffer) {
+ when(value) {
+ is VaultException.NotFound -> {
+ buf.putInt(1)
+ Unit
+ }
+ is VaultException.Serialization -> {
+ buf.putInt(2)
+ Unit
+ }
+ is VaultException.Crypto -> {
+ buf.putInt(3)
+ Unit
+ }
+ }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ }
+ }
+
+}
+
+
+
+
+/**
+ * @suppress
+ */
+public object FfiConverterOptionalBoolean: FfiConverterRustBuffer {
+ override fun read(buf: ByteBuffer): kotlin.Boolean? {
+ if (buf.get().toInt() == 0) {
+ return null
+ }
+ return FfiConverterBoolean.read(buf)
+ }
+
+ override fun allocationSize(value: kotlin.Boolean?): ULong {
+ if (value == null) {
+ return 1UL
+ } else {
+ return 1UL + FfiConverterBoolean.allocationSize(value)
+ }
+ }
+
+ override fun write(value: kotlin.Boolean?, buf: ByteBuffer) {
+ if (value == null) {
+ buf.put(0)
+ } else {
+ buf.put(1)
+ FfiConverterBoolean.write(value, buf)
+ }
+ }
+}
+
+
+
+
+/**
+ * @suppress
+ */
+public object FfiConverterOptionalString: FfiConverterRustBuffer {
+ override fun read(buf: ByteBuffer): kotlin.String? {
+ if (buf.get().toInt() == 0) {
+ return null
+ }
+ return FfiConverterString.read(buf)
+ }
+
+ override fun allocationSize(value: kotlin.String?): ULong {
+ if (value == null) {
+ return 1UL
+ } else {
+ return 1UL + FfiConverterString.allocationSize(value)
+ }
+ }
+
+ override fun write(value: kotlin.String?, buf: ByteBuffer) {
+ if (value == null) {
+ buf.put(0)
+ } else {
+ buf.put(1)
+ FfiConverterString.write(value, buf)
+ }
+ }
+}
+
+
+
+
+/**
+ * @suppress
+ */
+public object FfiConverterOptionalByteArray: FfiConverterRustBuffer {
+ override fun read(buf: ByteBuffer): kotlin.ByteArray? {
+ if (buf.get().toInt() == 0) {
+ return null
+ }
+ return FfiConverterByteArray.read(buf)
+ }
+
+ override fun allocationSize(value: kotlin.ByteArray?): ULong {
+ if (value == null) {
+ return 1UL
+ } else {
+ return 1UL + FfiConverterByteArray.allocationSize(value)
+ }
+ }
+
+ override fun write(value: kotlin.ByteArray?, buf: ByteBuffer) {
+ if (value == null) {
+ buf.put(0)
+ } else {
+ buf.put(1)
+ FfiConverterByteArray.write(value, buf)
+ }
+ }
+}
+
+
+
+
+/**
+ * @suppress
+ */
+public object FfiConverterOptionalTypeVaultItem: FfiConverterRustBuffer {
+ override fun read(buf: ByteBuffer): VaultItem? {
+ if (buf.get().toInt() == 0) {
+ return null
+ }
+ return FfiConverterTypeVaultItem.read(buf)
+ }
+
+ override fun allocationSize(value: VaultItem?): ULong {
+ if (value == null) {
+ return 1UL
+ } else {
+ return 1UL + FfiConverterTypeVaultItem.allocationSize(value)
+ }
+ }
+
+ override fun write(value: VaultItem?, buf: ByteBuffer) {
+ if (value == null) {
+ buf.put(0)
+ } else {
+ buf.put(1)
+ FfiConverterTypeVaultItem.write(value, buf)
+ }
+ }
+}
+
+
+
+
+/**
+ * @suppress
+ */
+public object FfiConverterOptionalSequenceString: FfiConverterRustBuffer?> {
+ override fun read(buf: ByteBuffer): List? {
+ if (buf.get().toInt() == 0) {
+ return null
+ }
+ return FfiConverterSequenceString.read(buf)
+ }
+
+ override fun allocationSize(value: List?): ULong {
+ if (value == null) {
+ return 1UL
+ } else {
+ return 1UL + FfiConverterSequenceString.allocationSize(value)
+ }
+ }
+
+ override fun write(value: List?, buf: ByteBuffer) {
+ if (value == null) {
+ buf.put(0)
+ } else {
+ buf.put(1)
+ FfiConverterSequenceString.write(value, buf)
+ }
+ }
+}
+
+
+
+
+/**
+ * @suppress
+ */
+public object FfiConverterSequenceString: FfiConverterRustBuffer> {
+ override fun read(buf: ByteBuffer): List {
+ val len = buf.getInt()
+ return List(len) {
+ FfiConverterString.read(buf)
+ }
+ }
+
+ override fun allocationSize(value: List): ULong {
+ val sizeForLength = 4UL
+ val sizeForItems = value.map { FfiConverterString.allocationSize(it) }.sum()
+ return sizeForLength + sizeForItems
+ }
+
+ override fun write(value: List, buf: ByteBuffer) {
+ buf.putInt(value.size)
+ value.iterator().forEach {
+ FfiConverterString.write(it, buf)
+ }
+ }
+}
+
+
+
+
+/**
+ * @suppress
+ */
+public object FfiConverterSequenceTypeVaultItem: FfiConverterRustBuffer> {
+ override fun read(buf: ByteBuffer): List {
+ val len = buf.getInt()
+ return List(len) {
+ FfiConverterTypeVaultItem.read(buf)
+ }
+ }
+
+ override fun allocationSize(value: List): ULong {
+ val sizeForLength = 4UL
+ val sizeForItems = value.map { FfiConverterTypeVaultItem.allocationSize(it) }.sum()
+ return sizeForLength + sizeForItems
+ }
+
+ override fun write(value: List, buf: ByteBuffer) {
+ buf.putInt(value.size)
+ value.iterator().forEach {
+ FfiConverterTypeVaultItem.write(it, buf)
+ }
+ }
+}
+ @Throws(CryptoException::class) fun `decrypt`(`ciphertext`: kotlin.ByteArray, `key`: kotlin.ByteArray): kotlin.ByteArray {
+ return FfiConverterByteArray.lift(
+ uniffiRustCallWithError(CryptoException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_func_decrypt(
+ FfiConverterByteArray.lower(`ciphertext`),FfiConverterByteArray.lower(`key`),_status)
+}
+ )
+ }
+
+
+ @Throws(CryptoException::class) fun `deriveAuk`(`password`: kotlin.String, `secretKey`: kotlin.String, `salt`: kotlin.ByteArray): kotlin.ByteArray {
+ return FfiConverterByteArray.lift(
+ uniffiRustCallWithError(CryptoException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_func_derive_auk(
+ FfiConverterString.lower(`password`),FfiConverterString.lower(`secretKey`),FfiConverterByteArray.lower(`salt`),_status)
+}
+ )
+ }
+
+
+ @Throws(CryptoException::class) fun `deriveItemKey`(`vaultKey`: kotlin.ByteArray, `itemId`: kotlin.String): kotlin.ByteArray {
+ return FfiConverterByteArray.lift(
+ uniffiRustCallWithError(CryptoException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_func_derive_item_key(
+ FfiConverterByteArray.lower(`vaultKey`),FfiConverterString.lower(`itemId`),_status)
+}
+ )
+ }
+
+
+ @Throws(CryptoException::class) fun `encrypt`(`plaintext`: kotlin.ByteArray, `key`: kotlin.ByteArray): kotlin.ByteArray {
+ return FfiConverterByteArray.lift(
+ uniffiRustCallWithError(CryptoException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_func_encrypt(
+ FfiConverterByteArray.lower(`plaintext`),FfiConverterByteArray.lower(`key`),_status)
+}
+ )
+ }
+
+ fun `generateItemId`(): kotlin.String {
+ return FfiConverterString.lift(
+ uniffiRustCall() { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_func_generate_item_id(
+ _status)
+}
+ )
+ }
+
+ fun `generateSalt`(): kotlin.ByteArray {
+ return FfiConverterByteArray.lift(
+ uniffiRustCall() { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_func_generate_salt(
+ _status)
+}
+ )
+ }
+
+ fun `generateSecretKey`(): kotlin.String {
+ return FfiConverterString.lift(
+ uniffiRustCall() { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_func_generate_secret_key(
+ _status)
+}
+ )
+ }
+
+ fun `generateVaultKey`(): kotlin.ByteArray {
+ return FfiConverterByteArray.lift(
+ uniffiRustCall() { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_func_generate_vault_key(
+ _status)
+}
+ )
+ }
+
+
+ @Throws(CryptoException::class) fun `unwrapVaultKey`(`wrapped`: kotlin.ByteArray, `auk`: kotlin.ByteArray): kotlin.ByteArray {
+ return FfiConverterByteArray.lift(
+ uniffiRustCallWithError(CryptoException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_func_unwrap_vault_key(
+ FfiConverterByteArray.lower(`wrapped`),FfiConverterByteArray.lower(`auk`),_status)
+}
+ )
+ }
+
+
+ @Throws(CryptoException::class) fun `wrapVaultKey`(`vaultKey`: kotlin.ByteArray, `auk`: kotlin.ByteArray): kotlin.ByteArray {
+ return FfiConverterByteArray.lift(
+ uniffiRustCallWithError(CryptoException) { _status ->
+ UniffiLib.INSTANCE.uniffi_noro_mobile_core_fn_func_wrap_vault_key(
+ FfiConverterByteArray.lower(`vaultKey`),FfiConverterByteArray.lower(`auk`),_status)
+}
+ )
+ }
+
+
+
diff --git a/apps/mobile-core/src/bindgen.rs b/apps/mobile-core/src/bindgen.rs
new file mode 100644
index 0000000..f6cff6c
--- /dev/null
+++ b/apps/mobile-core/src/bindgen.rs
@@ -0,0 +1,3 @@
+fn main() {
+ uniffi::uniffi_bindgen_main()
+}
diff --git a/apps/mobile-core/src/crypto.rs b/apps/mobile-core/src/crypto.rs
new file mode 100644
index 0000000..55a181d
--- /dev/null
+++ b/apps/mobile-core/src/crypto.rs
@@ -0,0 +1,54 @@
+use chacha20poly1305::{
+ aead::{Aead, KeyInit},
+ XChaCha20Poly1305, XNonce,
+};
+use rand::Rng;
+
+const NONCE_LEN: usize = 24;
+const KEY_LEN: usize = 32;
+
+pub fn encrypt(plaintext: &[u8], key: &[u8]) -> Result, super::CryptoError> {
+ if key.len() != KEY_LEN {
+ return Err(super::CryptoError::InvalidKeyLength);
+ }
+ let cipher =
+ XChaCha20Poly1305::new_from_slice(key).map_err(|_| super::CryptoError::Encryption)?;
+ let mut rng = rand::thread_rng();
+ let nonce_bytes: [u8; NONCE_LEN] = rng.gen();
+ let nonce = XNonce::from_slice(&nonce_bytes);
+ let ciphertext = cipher
+ .encrypt(nonce, plaintext)
+ .map_err(|_| super::CryptoError::Encryption)?;
+ let mut result = Vec::with_capacity(NONCE_LEN + ciphertext.len());
+ result.extend_from_slice(&nonce_bytes);
+ result.extend_from_slice(&ciphertext);
+ Ok(result)
+}
+
+pub fn decrypt(ciphertext: &[u8], key: &[u8]) -> Result, super::CryptoError> {
+ if key.len() != KEY_LEN {
+ return Err(super::CryptoError::InvalidKeyLength);
+ }
+ if ciphertext.len() < NONCE_LEN + 16 {
+ return Err(super::CryptoError::Decryption);
+ }
+ let cipher =
+ XChaCha20Poly1305::new_from_slice(key).map_err(|_| super::CryptoError::Decryption)?;
+ let nonce = XNonce::from_slice(&ciphertext[..NONCE_LEN]);
+ let plaintext = cipher
+ .decrypt(nonce, &ciphertext[NONCE_LEN..])
+ .map_err(|_| super::CryptoError::Decryption)?;
+ Ok(plaintext)
+}
+
+pub fn generate_key() -> Vec {
+ let mut rng = rand::thread_rng();
+ let key: [u8; KEY_LEN] = rng.gen();
+ key.to_vec()
+}
+
+pub fn generate_salt() -> Vec {
+ let mut rng = rand::thread_rng();
+ let salt: [u8; 16] = rng.gen();
+ salt.to_vec()
+}
diff --git a/apps/mobile-core/src/lib.rs b/apps/mobile-core/src/lib.rs
new file mode 100644
index 0000000..d088aa3
--- /dev/null
+++ b/apps/mobile-core/src/lib.rs
@@ -0,0 +1,253 @@
+mod crypto;
+mod sync;
+mod twoskd;
+mod vault;
+
+use std::sync::Arc;
+
+uniffi::setup_scaffolding!();
+
+#[derive(Debug, thiserror::Error, uniffi::Error)]
+pub enum CryptoError {
+ #[error("argon2 error")]
+ Argon2,
+ #[error("encryption error")]
+ Encryption,
+ #[error("decryption error")]
+ Decryption,
+ #[error("invalid secret key format")]
+ InvalidSecretKey,
+ #[error("invalid key length")]
+ InvalidKeyLength,
+}
+
+#[derive(Debug, thiserror::Error, uniffi::Error)]
+pub enum VaultError {
+ #[error("not found")]
+ NotFound,
+ #[error("serialization error")]
+ Serialization,
+ #[error("crypto error")]
+ Crypto,
+}
+
+#[derive(Debug, thiserror::Error, uniffi::Error)]
+pub enum SyncError {
+ #[error("http error")]
+ Http,
+ #[error("auth error")]
+ Auth,
+ #[error("conflict")]
+ Conflict,
+ #[error("parse error")]
+ Parse,
+}
+
+#[uniffi::export]
+pub fn generate_secret_key() -> String {
+ twoskd::generatesecretkey()
+}
+
+#[uniffi::export]
+pub fn derive_auk(password: String, secret_key: String, salt: Vec) -> Result