Гайд - Как написать библиотеку на Rust? Реальный пример | End Way - форум программирования и сливов различных скриптов
  • Присоединяйтесь к нам в телеграм канал! EndWay канал | EndSoft канал | EWStudio канал
  • Хочешь поставить скрипт, но не умеешь?
    А может ты хочешь свой скрипт на основе слитого?

    Тогда добро пожаловать в нашу студию разработки!

    Телеграм бот: EWStudioBot
    Телеграм канал: EWStudio

Гайд Как написать библиотеку на Rust? Реальный пример

Salat

EndWay
Автор темы
8 Янв 2023
7
11
0
Привет Rustacean:lap

Сегодня я покажу тебе как написать свою первую библиотеку, которая может быть реально нужна и будет полезна для твоего последывающего обучения как работать с API сервисов
Любая библиотека начинается с задумки - как решить поставленную проблему.
Изучая различия между IPv4 и IPv6 я столкнулся с главным преимуществом IPv6 для построения децентрализованных компьютерных систем - отсутствие ограничений NAT.
Но как же получить собственный IPv6 адрес программным способом? Казалось бы, все сервисы выдают тебе только IPv4 адрес. Поэтому, я решил найти похожий, но с IPv6 и нашел https://wtfismyip.com
К счастью у них оказалась возможность получения информации собственного IPv6 адреса и краткой информации о нем в формате JSON и обычного текста(текстом только IPv6 адрес)
Вдобавок, я решил, что сделаю возможность использовать её и асинхронно и синхронно

Для начала надо создать проект с помощью cargo и сразу добавить все необходимые зависимости
В примере библиотека названа "mynewlib", но вы можете назвать по другому:
cargo new mynewlib --lib && cd mynewlib
cargo add serde --features derive
cargo add reqwest --features blocking
cargo add serde_json

Теперь открываем папочку с проектом в любимом редакторе кода и начинаем писать код:brff

Создадим два файла src/async.rs и src/sync.rs для асинхронных и синхронных реализаций соответственно
В src/lib.rs укажем эти два файла как части библиотеки благодаря оператору mod и создадим структуру IpInfo, которую мы будем использовать для получения данных в формате JSON

К сожалению, поддержки Rust ещё нет, потому без syntax highlighting:
pub mod sync;
mod r#async;
pub use r#async::*;

use serde::Deserialize;
use std::net::Ipv6Addr;

#[derive(Deserialize, Debug, Clone)]
pub struct IpInfo {
    #[serde(rename = "YourFuckingIPAddress")]
    pub ip_address: Ipv6Addr,
    #[serde(rename = "YourFuckingLocation")]
    pub location: String,
    #[serde(rename = "YourFuckingHostname")]
    pub hostname: Ipv6Addr,
    #[serde(rename = "YourFuckingISP")]
    pub isp: String,
    #[serde(rename = "YourFuckingTorExit")]
    pub tor_exit: bool,
    #[serde(rename = "YourFuckingCity")]
    pub city: String,
    #[serde(rename = "YourFuckingCountry")]
    pub country: String,
    #[serde(rename = "YourFuckingCountryCode")]
    pub country_code: String
}

Строкой pub use sync; мы определяем, что модуль(в данном случае файл) src/sync.rs публичный, то есть пользователь, использующий библиотеку в своём проекте может использовать его, а не только сама библиотека
Следующие две строки определяют, что всё содержимое модуля src/async.rs должно считаться, как содержимое корня библиотеки, то есть src/lib.rs. Для чего это сделано? Асинхронная реализация зачастую более востребована, поэтому пусть она будет реализацией "по умолчанию".

Используем #[serde(rename)], чтобы поля в нашей структуре назывались коротко и локанично, но при этом парсер JSON на основе serde мог понять, что например поле ip_address надо заполнить информацией из YourFuckingIPAddress

Префикс r# в данном случае используется для того, чтобы компилятор воспринимал слово async за название модуля, а не оператор.
Используем модуль reqwest::blocking для синхронной реализации.
Функция get_ip_info возвращает "результат" - определённую нами структуру IpInfo или ошибку библиотеки reqwest. Таким образом, мы даём возможность пользователю самому обработать ошибку.
Функция get_ip тоже возвращает "результат", но вместо IpInfo возвращает Ipv6Addr(тип в стандартной библиотеке, для обработки IPv6 адресов) при успешном выполнении запроса. Функция strip_suffix используется, чтобы убрать переход на новую строку, потому что иначе получается ошибка при парсинге IPv6 адреса.

src/sync.rs:
use crate::IpInfo;
use std::net::Ipv6Addr;

pub fn get_ip_info() -> Result<IpInfo, reqwest::Error> {
    let body = reqwest::blocking::get("https://wtfismyip.com/json")?.text()?;
 
    Ok(serde_json::from_str(&body).unwrap())
}

pub fn get_ip() -> Result<Ipv6Addr, reqwest::Error> {
    let body = reqwest::blocking::get("https://wtfismyip.com/text")?.text()?
        .strip_suffix("\n").unwrap()
        .parse().unwrap();
 
    Ok(body)
}
В асинхронной версии всё абсолютно тоже самое, за исключением некоторых моментов.
Здесь мы уже можем увидеть что используются ключевые слова async/.await, а вместо reqwest::blocking::get используется асинхронный reqwest::get
src/async.rs:
use crate::IpInfo;
use std::net::Ipv6Addr;

pub async fn get_ip_info() -> Result<IpInfo, reqwest::Error> {
    let body = reqwest::get("https://wtfismyip.com/json").await?.text().await?;
 
    Ok(serde_json::from_str(&body).unwrap())
}

pub async fn get_ip() -> Result<Ipv6Addr, reqwest::Error> {
    let body = reqwest::get("https://wtfismyip.com/text").await?.text().await?
        .strip_suffix("\n").unwrap()
        .parse().unwrap();
    
    Ok(body)
}

В будущем я планирую добавить поддержку нескольких других http-клиентов для этой библиотеки, которые вы сами сможете выбрать благодаря features. Все изменения можно будет отследить тут:fox

 
Последнее редактирование модератором:
Like
  • 3
Реакции: 2 users
Активность:
Пока что здесь никого нет