Group rust ffi code (#1893)
* Group rust ffi code Signed-off-by: Dude so hot <djohn@fbi.gov> * Fix Signed-off-by: Dude so hot <djohn@fbi.gov> * Fix Signed-off-by: Dude so hot <djohn@fbi.gov> * Refine Signed-off-by: Dude so hot <djohn@fbi.gov> --------- Signed-off-by: Dude so hot <djohn@fbi.gov> Co-authored-by: Dude so hot <djohn@fbi.gov>
This commit is contained in:
1 parent
a566393a89
commit
45a5bae8b2
16 files changed
+246
-241
No files matched your search
@@ -111,7 +111,9 @@ FetchContent_MakeAvailable(Corrosion)
|
||||
corrosion_import_crate(MANIFEST_PATH ../rust/Cargo.toml LOCKED)
|
||||
corrosion_add_target_local_rustflags(ehviewer_rust "-Clink-args=-Wl,-z,max-page-size=16384")
|
||||
if (ANDROID_NATIVE_API_LEVEL GREATER_EQUAL 26)
|
||||
corrosion_set_features(ehviewer_rust FEATURES api-level-26)
|
||||
corrosion_set_features(ehviewer_rust FEATURES android-26)
|
||||
else ()
|
||||
corrosion_set_features(ehviewer_rust FEATURES android)
|
||||
endif ()
|
||||
|
||||
# Build and link our app's native lib
|
||||
|
||||
@@ -7,12 +7,14 @@ edition = "2021"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[features]
|
||||
api-level-26 = ["ndk/api-level-26"]
|
||||
jvm = ["dep:jni", "dep:jni_fn"]
|
||||
android = ["dep:ndk", "jvm", "ndk/bitmap"]
|
||||
android-26 = ["android", "ndk/api-level-26"]
|
||||
|
||||
[dependencies]
|
||||
regex-lite = "0.1"
|
||||
jni = "0.21"
|
||||
jni_fn = "0.1"
|
||||
jni = { version = "0.21", optional = true }
|
||||
jni_fn = { version = "0.1", optional = true }
|
||||
quick-xml = { default-features = false, version = "0.37" }
|
||||
tl = "0.7"
|
||||
log = "0.4"
|
||||
@@ -22,7 +24,7 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
anyhow = "1.0"
|
||||
image = { default-features = false, version = "0.25" }
|
||||
rxing = { default-features = false, version = "0.6" }
|
||||
ndk = { version = "0.9", features = ["bitmap"] }
|
||||
ndk = { version = "0.9", optional = true }
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
mod android;
|
||||
mod android_o;
|
||||
mod jvm;
|
||||
@@ -0,0 +1,55 @@
|
||||
#![cfg(feature = "android")]
|
||||
|
||||
use super::jvm::jni_throwing;
|
||||
use crate::img::border::detect_border;
|
||||
use crate::img::qr_code::detect_image_ad;
|
||||
use anyhow::{Context, Result};
|
||||
use image::{ImageBuffer, Rgba};
|
||||
use jni::objects::JClass;
|
||||
use jni::sys::jboolean;
|
||||
use jni::sys::{jintArray, jobject};
|
||||
use jni::JNIEnv;
|
||||
use jni_fn::jni_fn;
|
||||
use ndk::bitmap::Bitmap;
|
||||
use std::ptr::slice_from_raw_parts;
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
#[jni_fn("com.hippo.ehviewer.image.ImageKt")]
|
||||
pub fn detectBorder(mut env: JNIEnv, _class: JClass, object: jobject) -> jintArray {
|
||||
jni_throwing(&mut env, |env| {
|
||||
let slice = with_bitmap_content(env, object, |img| Ok(detect_border(&img)))?;
|
||||
let array = env.new_int_array(4)?;
|
||||
env.set_int_array_region(&array, 0, &slice.context("Image too small!")?)?;
|
||||
Ok(array.into_raw())
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
#[jni_fn("com.hippo.ehviewer.image.ImageKt")]
|
||||
pub fn hasQrCode(mut env: JNIEnv, _class: JClass, object: jobject) -> jboolean {
|
||||
jni_throwing(&mut env, |env| {
|
||||
with_bitmap_content(env, object, |image| Ok(detect_image_ad(image) as jboolean))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_bitmap_content<F, R>(env: &mut JNIEnv, bitmap: jobject, f: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(ImageBuffer<Rgba<u8>, &[u8]>) -> Result<R>,
|
||||
{
|
||||
// SAFETY: kotlin caller must ensure bitmap is valid.
|
||||
let handle = unsafe { Bitmap::from_jni(env.get_raw(), bitmap) };
|
||||
|
||||
let info = handle.info()?;
|
||||
let (width, height) = (info.width(), info.height());
|
||||
let ptr = handle.lock_pixels()? as *const u8;
|
||||
|
||||
// SAFETY: maybe unsafe if bitmap buffer not RGBA8888 format.
|
||||
let buffer = unsafe { &*slice_from_raw_parts(ptr, (width * height * 4) as usize) };
|
||||
|
||||
let image = ImageBuffer::from_raw(width, height, buffer);
|
||||
let result = image.context("Image buffer not RGBA8888!!!").and_then(f);
|
||||
handle.unlock_pixels()?;
|
||||
result
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#![cfg(feature = "api-level-26")]
|
||||
#![cfg(feature = "android-26")]
|
||||
|
||||
use crate::{jni_throwing, with_bitmap_content};
|
||||
use super::android::with_bitmap_content;
|
||||
use super::jvm::jni_throwing;
|
||||
use anyhow::Context;
|
||||
use image::{GenericImage, GenericImageView, ImageBuffer};
|
||||
use jni::objects::JClass;
|
||||
@@ -0,0 +1,143 @@
|
||||
#![cfg(feature = "jvm")]
|
||||
|
||||
use crate::parser::config::parse_fav_cat;
|
||||
use crate::parser::fav::parse_fav;
|
||||
use crate::parser::home::parse_limit;
|
||||
use crate::parser::list::parse_info_list;
|
||||
use crate::parser::torrent::parse_torrent_list;
|
||||
use crate::EhError;
|
||||
use android_logger::Config;
|
||||
use anyhow::{ensure, Context, Result};
|
||||
use jni::objects::{JByteBuffer, JClass};
|
||||
use jni::sys::{jboolean, jint, jobject, JNI_VERSION_1_6};
|
||||
use jni::{JNIEnv, JavaVM};
|
||||
use jni_fn::jni_fn;
|
||||
use log::LevelFilter;
|
||||
use serde::Serialize;
|
||||
use std::ffi::c_void;
|
||||
use std::io::Cursor;
|
||||
use std::ptr::slice_from_raw_parts_mut;
|
||||
use std::str::from_utf8_unchecked;
|
||||
use tl::{ParserOptions, VDom};
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
#[jni_fn("com.hippo.ehviewer.client.parser.FavoritesParserKt")]
|
||||
pub fn parseFav(mut env: JNIEnv, _class: JClass, input: JByteBuffer, limit: jint) -> jint {
|
||||
parse_marshal_inplace(&mut env, input, limit, |dom, html| {
|
||||
parse_fav(dom, dom.parser(), html)
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
#[jni_fn("com.hippo.ehviewer.client.parser.HomeParserKt")]
|
||||
pub fn parseLimit(mut env: JNIEnv, _class: JClass, input: JByteBuffer, limit: jint) -> jint {
|
||||
parse_marshal_inplace(&mut env, input, limit, |dom, _| {
|
||||
parse_limit(dom, dom.parser()).context("Can't parse Limit")
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
#[jni_fn("com.hippo.ehviewer.client.parser.GalleryListParserKt")]
|
||||
pub fn parseGalleryInfoList(mut env: JNIEnv, _: JClass, buffer: JByteBuffer, limit: jint) -> jint {
|
||||
parse_marshal_inplace(&mut env, buffer, limit, |dom, str| {
|
||||
parse_info_list(dom, dom.parser(), str)
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
#[jni_fn("com.hippo.ehviewer.client.parser.TorrentParserKt")]
|
||||
pub fn parseTorrent(mut env: JNIEnv, _class: JClass, buffer: JByteBuffer, limit: jint) -> jint {
|
||||
parse_marshal_inplace(&mut env, buffer, limit, |dom, _| {
|
||||
parse_torrent_list(dom, dom.parser())
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
#[jni_fn("com.hippo.ehviewer.client.parser.UserConfigParser")]
|
||||
pub fn parseFavCat(mut env: JNIEnv, _class: JClass, buffer: JByteBuffer, limit: jint) -> jint {
|
||||
parse_marshal_inplace(&mut env, buffer, limit, |_, body| Ok(parse_fav_cat(body)))
|
||||
}
|
||||
|
||||
fn deref_mut_direct_bytebuffer<'local>(
|
||||
env: &JNIEnv,
|
||||
buffer: JByteBuffer<'local>,
|
||||
) -> Result<&'local mut [u8]> {
|
||||
let ptr = env.get_direct_buffer_address(&buffer)?;
|
||||
let cap = env.get_direct_buffer_capacity(&buffer)?;
|
||||
|
||||
// SAFETY: jni contract, buffer must alive through native call.
|
||||
Ok(unsafe { &mut *slice_from_raw_parts_mut(ptr, cap) })
|
||||
}
|
||||
|
||||
pub trait ThrowingHasDefault {
|
||||
fn default() -> Self;
|
||||
}
|
||||
|
||||
impl ThrowingHasDefault for jboolean {
|
||||
fn default() -> Self {
|
||||
0 as jboolean
|
||||
}
|
||||
}
|
||||
|
||||
impl ThrowingHasDefault for jobject {
|
||||
fn default() -> Self {
|
||||
0 as jobject
|
||||
}
|
||||
}
|
||||
|
||||
impl ThrowingHasDefault for i32 {
|
||||
fn default() -> Self {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl ThrowingHasDefault for () {
|
||||
fn default() -> Self {}
|
||||
}
|
||||
|
||||
pub fn jni_throwing<F, R>(env: &mut JNIEnv, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut JNIEnv) -> Result<R>,
|
||||
R: ThrowingHasDefault,
|
||||
{
|
||||
match f(env) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
let msg = format!("{err}");
|
||||
env.throw_new("java/lang/RuntimeException", msg).ok();
|
||||
R::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_marshal_inplace<F, R>(env: &mut JNIEnv, str: JByteBuffer, limit: jint, f: F) -> i32
|
||||
where
|
||||
F: Fn(&VDom, &str) -> Result<R>,
|
||||
R: Serialize,
|
||||
{
|
||||
jni_throwing(env, |env| {
|
||||
let buffer = deref_mut_direct_bytebuffer(env, str)?;
|
||||
let value = {
|
||||
// SAFETY: ktor client ensure html content is valid utf-8.
|
||||
let html = unsafe { from_utf8_unchecked(&buffer[..limit as usize]) };
|
||||
|
||||
let dom = tl::parse(html, ParserOptions::default())?;
|
||||
ensure!(dom.version().is_some(), EhError::Error(html.to_string()));
|
||||
f(&dom, html)?
|
||||
};
|
||||
let mut cursor = Cursor::new(buffer);
|
||||
serde_cbor::to_writer(&mut cursor, &value)?;
|
||||
Ok(cursor.position() as i32)
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn JNI_OnLoad(_: JavaVM, _: *mut c_void) -> jint {
|
||||
android_logger::init_once(Config::default().with_max_level(LevelFilter::Debug));
|
||||
JNI_VERSION_1_6
|
||||
}
|
||||
@@ -1,3 +1,2 @@
|
||||
mod border;
|
||||
mod copy_ahb;
|
||||
mod qr_code;
|
||||
pub mod border;
|
||||
pub mod qr_code;
|
||||
@@ -1,10 +1,4 @@
|
||||
use crate::{jni_throwing, with_bitmap_content};
|
||||
use anyhow::Context;
|
||||
use image::{ImageBuffer, Luma, Pixel, Rgba};
|
||||
use jni::objects::JClass;
|
||||
use jni::sys::{jintArray, jobject};
|
||||
use jni::JNIEnv;
|
||||
use jni_fn::jni_fn;
|
||||
|
||||
/** A line will be considered as having content if 0.25% of it is filled. */
|
||||
static FILLED_RATIO_LIMIT: f32 = 0.0025;
|
||||
@@ -72,7 +66,8 @@ fn detect_border_lines<'pixel>(
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_border(image: &ImageBuffer<Rgba<u8>, &[u8]>) -> Option<[i32; 4]> {
|
||||
#[allow(dead_code)]
|
||||
pub fn detect_border(image: &ImageBuffer<Rgba<u8>, &[u8]>) -> Option<[i32; 4]> {
|
||||
let (w, h) = image.dimensions();
|
||||
let top = detect_border_lines(image.rows(), w);
|
||||
let bottom = detect_border_lines(image.rows().rev(), w);
|
||||
@@ -81,15 +76,3 @@ fn detect_border(image: &ImageBuffer<Rgba<u8>, &[u8]>) -> Option<[i32; 4]> {
|
||||
let right = detect_border_lines(iter.rev(), h);
|
||||
Some([left, top, w as i32 - right, h as i32 - bottom])
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
#[jni_fn("com.hippo.ehviewer.image.ImageKt")]
|
||||
pub fn detectBorder(mut env: JNIEnv, _class: JClass, object: jobject) -> jintArray {
|
||||
jni_throwing(&mut env, |env| {
|
||||
let slice = with_bitmap_content(env, object, |img| Ok(detect_border(&img)))?;
|
||||
let array = env.new_int_array(4)?;
|
||||
env.set_int_array_region(&array, 0, &slice.context("Image too small!")?)?;
|
||||
Ok(array.into_raw())
|
||||
})
|
||||
}
|
||||
@@ -1,16 +1,12 @@
|
||||
use crate::{jni_throwing, with_bitmap_content};
|
||||
use image::{ImageBuffer, Rgba};
|
||||
use jni::objects::JClass;
|
||||
use jni::sys::{jboolean, jobject};
|
||||
use jni::JNIEnv;
|
||||
use jni_fn::jni_fn;
|
||||
use rxing::common::HybridBinarizer;
|
||||
use rxing::qrcode::detector::FinderPatternFinder;
|
||||
use rxing::{BinaryBitmap, DecodingHintDictionary, Point, RGBLuminanceSource};
|
||||
use std::f32::consts::FRAC_1_SQRT_2;
|
||||
use std::ptr::slice_from_raw_parts;
|
||||
|
||||
fn detect_image_ad(image: ImageBuffer<Rgba<u8>, &[u8]>) -> bool {
|
||||
#[allow(dead_code)]
|
||||
pub fn detect_image_ad(image: ImageBuffer<Rgba<u8>, &[u8]>) -> bool {
|
||||
let (width, height) = (image.width() as usize, image.height() as usize);
|
||||
let buffer = image.into_raw();
|
||||
let pixels = unsafe { &*slice_from_raw_parts(buffer.as_ptr() as *const u32, buffer.len()) };
|
||||
@@ -30,12 +26,3 @@ fn detect_image_ad(image: ImageBuffer<Rgba<u8>, &[u8]>) -> bool {
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
#[jni_fn("com.hippo.ehviewer.image.ImageKt")]
|
||||
pub fn hasQrCode(mut env: JNIEnv, _class: JClass, object: jobject) -> jboolean {
|
||||
jni_throwing(&mut env, |env| {
|
||||
with_bitmap_content(env, object, |image| Ok(detect_image_ad(image) as jboolean))
|
||||
})
|
||||
}
|
||||
@@ -1,21 +1,8 @@
|
||||
mod ffi;
|
||||
mod img;
|
||||
mod parser;
|
||||
|
||||
use android_logger::Config;
|
||||
use anyhow::{ensure, Context, Result};
|
||||
use image::{ImageBuffer, Rgba};
|
||||
use jni::objects::JByteBuffer;
|
||||
use jni::sys::{jboolean, jint, jobject, JavaVM, JNI_VERSION_1_6};
|
||||
use jni::JNIEnv;
|
||||
use log::LevelFilter;
|
||||
use ndk::bitmap::Bitmap;
|
||||
use serde::Serialize;
|
||||
use std::ffi::c_void;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::io::Cursor;
|
||||
use std::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut};
|
||||
use std::str::from_utf8_unchecked;
|
||||
use tl::ParserOptions;
|
||||
use tl::{Bytes, Node, NodeHandle, Parser, VDom};
|
||||
|
||||
#[macro_export]
|
||||
@@ -58,43 +45,6 @@ where
|
||||
handle.get(parser)
|
||||
}
|
||||
|
||||
fn deref_mut_direct_bytebuffer<'local>(
|
||||
env: &JNIEnv,
|
||||
buffer: JByteBuffer<'local>,
|
||||
) -> Result<&'local mut [u8]> {
|
||||
let ptr = env.get_direct_buffer_address(&buffer)?;
|
||||
let cap = env.get_direct_buffer_capacity(&buffer)?;
|
||||
|
||||
// SAFETY: jni contract, buffer must alive through native call.
|
||||
Ok(unsafe { &mut *slice_from_raw_parts_mut(ptr, cap) })
|
||||
}
|
||||
|
||||
trait ThrowingHasDefault {
|
||||
fn default() -> Self;
|
||||
}
|
||||
|
||||
impl ThrowingHasDefault for jboolean {
|
||||
fn default() -> Self {
|
||||
0 as jboolean
|
||||
}
|
||||
}
|
||||
|
||||
impl ThrowingHasDefault for jobject {
|
||||
fn default() -> Self {
|
||||
0 as jobject
|
||||
}
|
||||
}
|
||||
|
||||
impl ThrowingHasDefault for i32 {
|
||||
fn default() -> Self {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl ThrowingHasDefault for () {
|
||||
fn default() -> Self {}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum EhError {
|
||||
NoHits,
|
||||
@@ -115,42 +65,6 @@ impl Display for EhError {
|
||||
}
|
||||
}
|
||||
|
||||
fn jni_throwing<F, R>(env: &mut JNIEnv, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut JNIEnv) -> Result<R>,
|
||||
R: ThrowingHasDefault,
|
||||
{
|
||||
match f(env) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
let msg = format!("{err}");
|
||||
env.throw_new("java/lang/RuntimeException", msg).ok();
|
||||
R::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_marshal_inplace<F, R>(env: &mut JNIEnv, str: JByteBuffer, limit: jint, f: F) -> i32
|
||||
where
|
||||
F: Fn(&VDom, &str) -> Result<R>,
|
||||
R: Serialize,
|
||||
{
|
||||
jni_throwing(env, |env| {
|
||||
let buffer = deref_mut_direct_bytebuffer(env, str)?;
|
||||
let value = {
|
||||
// SAFETY: ktor client ensure html content is valid utf-8.
|
||||
let html = unsafe { from_utf8_unchecked(&buffer[..limit as usize]) };
|
||||
|
||||
let dom = tl::parse(html, ParserOptions::default())?;
|
||||
ensure!(dom.version().is_some(), EhError::Error(html.to_string()));
|
||||
f(&dom, html)?
|
||||
};
|
||||
let mut cursor = Cursor::new(buffer);
|
||||
serde_cbor::to_writer(&mut cursor, &value)?;
|
||||
Ok(cursor.position() as i32)
|
||||
})
|
||||
}
|
||||
|
||||
fn get_node_handle_attr<'a>(
|
||||
node: &NodeHandle,
|
||||
parser: &'a Parser,
|
||||
@@ -173,29 +87,3 @@ fn query_childs_first_match_attr<'a>(
|
||||
let mut iter = node.as_tag()?.query_selector(parser, &selector)?;
|
||||
get_node_handle_attr(&iter.next()?, parser, attr)
|
||||
}
|
||||
|
||||
fn with_bitmap_content<F, R>(env: &mut JNIEnv, bitmap: jobject, f: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(ImageBuffer<Rgba<u8>, &[u8]>) -> Result<R>,
|
||||
{
|
||||
// SAFETY: kotlin caller must ensure bitmap is valid.
|
||||
let handle = unsafe { Bitmap::from_jni(env.get_raw(), bitmap) };
|
||||
|
||||
let info = handle.info()?;
|
||||
let (width, height) = (info.width(), info.height());
|
||||
let ptr = handle.lock_pixels()? as *const u8;
|
||||
|
||||
// SAFETY: maybe unsafe if bitmap buffer not RGBA8888 format.
|
||||
let buffer = unsafe { &*slice_from_raw_parts(ptr, (width * height * 4) as usize) };
|
||||
|
||||
let image = ImageBuffer::from_raw(width, height, buffer);
|
||||
let result = image.context("Image buffer not RGBA8888!!!").and_then(f);
|
||||
handle.unlock_pixels()?;
|
||||
result
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn JNI_OnLoad(_: JavaVM, _: *mut c_void) -> jint {
|
||||
android_logger::init_once(Config::default().with_max_level(LevelFilter::Debug));
|
||||
JNI_VERSION_1_6
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
mod config;
|
||||
mod fav;
|
||||
mod home;
|
||||
mod list;
|
||||
mod torrent;
|
||||
pub mod config;
|
||||
pub mod fav;
|
||||
pub mod home;
|
||||
pub mod list;
|
||||
pub mod torrent;
|
||||
@@ -1,19 +1,10 @@
|
||||
use crate::{parse_marshal_inplace, regex};
|
||||
use jni::objects::{JByteBuffer, JClass};
|
||||
use jni::sys::jint;
|
||||
use jni::JNIEnv;
|
||||
use jni_fn::jni_fn;
|
||||
use crate::regex;
|
||||
use quick_xml::escape::unescape;
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
#[jni_fn("com.hippo.ehviewer.client.parser.UserConfigParser")]
|
||||
pub fn parseFavCat(mut env: JNIEnv, _class: JClass, buffer: JByteBuffer, limit: jint) -> jint {
|
||||
parse_marshal_inplace(&mut env, buffer, limit, |_, body| {
|
||||
let cat = regex!("<input type=\"text\" name=\"favorite_\\d\" value=\"([^\"]+)\"")
|
||||
.captures_iter(body)
|
||||
.filter_map(|c| unescape(&c[1]).map(|s| s.to_string()).ok())
|
||||
.collect::<Vec<_>>();
|
||||
Ok(cat)
|
||||
})
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_fav_cat(html: &str) -> Vec<String> {
|
||||
regex!("<input type=\"text\" name=\"favorite_\\d\" value=\"([^\"]+)\"")
|
||||
.captures_iter(html)
|
||||
.filter_map(|c| unescape(&c[1]).map(|s| s.to_string()).ok())
|
||||
.collect()
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
use crate::parser::list::{parse_info_list, GalleryListResult};
|
||||
use crate::{parse_marshal_inplace, EhError};
|
||||
use crate::EhError;
|
||||
use anyhow::{bail, Result};
|
||||
use jni::objects::{JByteBuffer, JClass};
|
||||
use jni::sys::jint;
|
||||
use jni::JNIEnv;
|
||||
use jni_fn::jni_fn;
|
||||
use quick_xml::escape::unescape;
|
||||
use serde::Serialize;
|
||||
use tl::Parser;
|
||||
@@ -12,13 +8,14 @@ use tl::VDom;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[allow(non_snake_case)]
|
||||
struct FavResult {
|
||||
pub struct FavResult {
|
||||
catArray: Vec<String>,
|
||||
countArray: Vec<i32>,
|
||||
galleryListResult: GalleryListResult,
|
||||
}
|
||||
|
||||
fn parse_fav(dom: &VDom, parser: &Parser, html: &str) -> Result<FavResult> {
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_fav(dom: &VDom, parser: &Parser, html: &str) -> Result<FavResult> {
|
||||
if html.contains("This page requires you to log on.</p>") {
|
||||
bail!(EhError::NeedLogin)
|
||||
}
|
||||
@@ -53,12 +50,3 @@ fn parse_fav(dom: &VDom, parser: &Parser, html: &str) -> Result<FavResult> {
|
||||
bail!("Illegal fav cat count!")
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
#[jni_fn("com.hippo.ehviewer.client.parser.FavoritesParserKt")]
|
||||
pub fn parseFav(mut env: JNIEnv, _class: JClass, input: JByteBuffer, limit: jint) -> jint {
|
||||
parse_marshal_inplace(&mut env, input, limit, |dom, html| {
|
||||
parse_fav(dom, dom.parser(), html)
|
||||
})
|
||||
}
|
||||
@@ -1,22 +1,18 @@
|
||||
use crate::{get_vdom_first_element_by_class_name, parse_marshal_inplace};
|
||||
use anyhow::Context;
|
||||
use jni::objects::{JByteBuffer, JClass};
|
||||
use jni::sys::jint;
|
||||
use jni::JNIEnv;
|
||||
use jni_fn::jni_fn;
|
||||
use crate::get_vdom_first_element_by_class_name;
|
||||
use serde::Serialize;
|
||||
use tl::Parser;
|
||||
use tl::VDom;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Serialize)]
|
||||
struct Limits {
|
||||
pub struct Limits {
|
||||
current: i32,
|
||||
maximum: i32,
|
||||
resetCost: i32,
|
||||
}
|
||||
|
||||
fn parse_limit(dom: &VDom, parser: &Parser) -> Option<Limits> {
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_limit(dom: &VDom, parser: &Parser) -> Option<Limits> {
|
||||
let limit_box = get_vdom_first_element_by_class_name(dom, "homebox")?.as_tag()?;
|
||||
let vec = limit_box
|
||||
.query_selector(parser, "strong")?
|
||||
@@ -46,12 +42,3 @@ fn parse_limit(dom: &VDom, parser: &Parser) -> Option<Limits> {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
#[jni_fn("com.hippo.ehviewer.client.parser.HomeParserKt")]
|
||||
pub fn parseLimit(mut env: JNIEnv, _class: JClass, input: JByteBuffer, limit: jint) -> jint {
|
||||
parse_marshal_inplace(&mut env, input, limit, |dom, _| {
|
||||
parse_limit(dom, dom.parser()).context("Can't parse Limit")
|
||||
})
|
||||
}
|
||||
@@ -1,13 +1,9 @@
|
||||
use crate::EhError;
|
||||
use crate::{get_element_by_id, get_vdom_first_element_by_class_name};
|
||||
use crate::{get_first_element_by_class_name, query_childs_first_match_attr};
|
||||
use crate::{get_node_attr, get_node_handle_attr, regex};
|
||||
use crate::{parse_marshal_inplace, EhError};
|
||||
use crate::{EHGT_PREFIX, EX_PREFIX};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use jni::objects::{JByteBuffer, JClass};
|
||||
use jni::sys::jint;
|
||||
use jni::JNIEnv;
|
||||
use jni_fn::jni_fn;
|
||||
use quick_xml::escape::unescape;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
@@ -197,6 +193,7 @@ fn parse_gallery_info(node: &Node, parser: &Parser) -> Option<BaseGalleryInfo> {
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_info_list(dom: &VDom, parser: &Parser, str: &str) -> Result<GalleryListResult> {
|
||||
if str.contains("<p>You do not have any watched tags") {
|
||||
bail!(EhError::NoWatched)
|
||||
@@ -241,12 +238,3 @@ pub fn parse_info_list(dom: &VDom, parser: &Parser, str: &str) -> Result<Gallery
|
||||
};
|
||||
f().context("No content")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
#[jni_fn("com.hippo.ehviewer.client.parser.GalleryListParserKt")]
|
||||
pub fn parseGalleryInfoList(mut env: JNIEnv, _: JClass, buffer: JByteBuffer, limit: jint) -> jint {
|
||||
parse_marshal_inplace(&mut env, buffer, limit, |dom, str| {
|
||||
parse_info_list(dom, dom.parser(), str)
|
||||
})
|
||||
}
|
||||
@@ -1,15 +1,11 @@
|
||||
use crate::{parse_marshal_inplace, regex};
|
||||
use crate::regex;
|
||||
use anyhow::{Context, Result};
|
||||
use jni::objects::{JByteBuffer, JClass};
|
||||
use jni::sys::jint;
|
||||
use jni::JNIEnv;
|
||||
use jni_fn::jni_fn;
|
||||
use quick_xml::escape::unescape;
|
||||
use serde::Serialize;
|
||||
use tl::{Parser, VDom};
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Torrent {
|
||||
pub struct Torrent {
|
||||
outdated: bool,
|
||||
posted: String,
|
||||
size: String,
|
||||
@@ -21,7 +17,8 @@ struct Torrent {
|
||||
name: String,
|
||||
}
|
||||
|
||||
fn parse_torrent_list(dom: &VDom, parser: &Parser) -> Result<Vec<Torrent>> {
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_torrent_list(dom: &VDom, parser: &Parser) -> Result<Vec<Torrent>> {
|
||||
let list = dom.query_selector("table").context("No Table")?.filter_map(|e| {
|
||||
let html = e.get(parser)?.inner_html(parser);
|
||||
if html.contains("Expunged") {
|
||||
@@ -44,12 +41,3 @@ fn parse_torrent_list(dom: &VDom, parser: &Parser) -> Result<Vec<Torrent>> {
|
||||
}).collect::<Vec<Torrent>>();
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
#[jni_fn("com.hippo.ehviewer.client.parser.TorrentParserKt")]
|
||||
pub fn parseTorrent(mut env: JNIEnv, _class: JClass, buffer: JByteBuffer, limit: jint) -> jint {
|
||||
parse_marshal_inplace(&mut env, buffer, limit, |dom, _| {
|
||||
parse_torrent_list(dom, dom.parser())
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user