synpad 0.1.0

A full-featured Matrix chat client built with Dioxus
/// Parse a Matrix permalink and extract the room ID and optional event ID.
///
/// Supports formats:
/// - `https://matrix.to/#/!roomid:server` → (room_id, None)
/// - `https://matrix.to/#/!roomid:server/$eventid` → (room_id, Some(event_id))
/// - `#room:server` → alias (not yet resolved)
/// - `!roomid:server` → (room_id, None)
pub fn parse_permalink(link: &str) -> Option<PermalinkTarget> {
    let trimmed = link.trim();

    // matrix.to links
    if trimmed.starts_with("https://matrix.to/#/") || trimmed.starts_with("http://matrix.to/#/") {
        let fragment = trimmed.split("/#/").nth(1)?;
        let decoded = urlencoding::decode(fragment).ok()?;
        let parts: Vec<&str> = decoded.splitn(2, '/').collect();

        if let Some(first) = parts.first() {
            // Strip query parameters
            let first = first.split('?').next().unwrap_or(first);
            if first.starts_with('!') {
                let room_id = first.to_string();
                let event_id = parts.get(1).and_then(|e| {
                    let e = e.split('?').next().unwrap_or(e);
                    if e.starts_with('$') {
                        Some(e.to_string())
                    } else {
                        None
                    }
                });
                return Some(PermalinkTarget::RoomId {
                    room_id,
                    event_id,
                });
            } else if first.starts_with('#') {
                return Some(PermalinkTarget::RoomAlias {
                    alias: first.to_string(),
                });
            } else if first.starts_with('@') {
                return Some(PermalinkTarget::UserId {
                    user_id: first.to_string(),
                });
            }
        }
    }

    // Direct IDs
    if trimmed.starts_with('!') {
        return Some(PermalinkTarget::RoomId {
            room_id: trimmed.to_string(),
            event_id: None,
        });
    }
    if trimmed.starts_with('#') {
        return Some(PermalinkTarget::RoomAlias {
            alias: trimmed.to_string(),
        });
    }
    if trimmed.starts_with('@') {
        return Some(PermalinkTarget::UserId {
            user_id: trimmed.to_string(),
        });
    }

    None
}

/// Target of a parsed permalink.
#[derive(Clone, Debug, PartialEq)]
pub enum PermalinkTarget {
    RoomId {
        room_id: String,
        event_id: Option<String>,
    },
    RoomAlias {
        alias: String,
    },
    UserId {
        user_id: String,
    },
}

/// Generate a matrix.to permalink for a room.
pub fn make_room_permalink(room_id: &str) -> String {
    format!("https://matrix.to/#/{}", urlencoding::encode(room_id))
}

/// Generate a matrix.to permalink for an event in a room.
pub fn make_event_permalink(room_id: &str, event_id: &str) -> String {
    format!(
        "https://matrix.to/#/{}/{}",
        urlencoding::encode(room_id),
        urlencoding::encode(event_id)
    )
}

/// Generate a matrix.to permalink for a user.
pub fn make_user_permalink(user_id: &str) -> String {
    format!("https://matrix.to/#/{}", urlencoding::encode(user_id))
}