feat: set up FFI
Signed-off-by: Sphericalkat <me@kat.bio>
This commit is contained in:
commit
a5b8aa83d6
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
||||
|
||||
# Avoid committing pubspec.lock for library packages; see
|
||||
# https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
pubspec.lock
|
||||
|
||||
# FVM Version Cache
|
||||
.fvm/
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"dart.flutterSdkPath": ".fvm/versions/stable"
|
||||
}
|
3
CHANGELOG.md
Normal file
3
CHANGELOG.md
Normal file
@ -0,0 +1,3 @@
|
||||
## 1.0.0
|
||||
|
||||
- Initial version.
|
39
README.md
Normal file
39
README.md
Normal file
@ -0,0 +1,39 @@
|
||||
<!--
|
||||
This README describes the package. If you publish this package to pub.dev,
|
||||
this README's contents appear on the landing page for your package.
|
||||
|
||||
For information about how to write a good package README, see the guide for
|
||||
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
|
||||
|
||||
For general information about developing packages, see the Dart guide for
|
||||
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
|
||||
and the Flutter guide for
|
||||
[developing packages and plugins](https://flutter.dev/developing-packages).
|
||||
-->
|
||||
|
||||
TODO: Put a short description of the package here that helps potential users
|
||||
know whether this package might be useful for them.
|
||||
|
||||
## Features
|
||||
|
||||
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
||||
|
||||
## Getting started
|
||||
|
||||
TODO: List prerequisites and provide or point to information on how to
|
||||
start using the package.
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Include short and useful examples for package users. Add longer examples
|
||||
to `/example` folder.
|
||||
|
||||
```dart
|
||||
const like = 'sample';
|
||||
```
|
||||
|
||||
## Additional information
|
||||
|
||||
TODO: Tell users more about the package: where to find more information, how to
|
||||
contribute to the package, how to file issues, what response they can expect
|
||||
from the package authors, and more.
|
30
analysis_options.yaml
Normal file
30
analysis_options.yaml
Normal file
@ -0,0 +1,30 @@
|
||||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
7
example/readability_example.dart
Normal file
7
example/readability_example.dart
Normal file
@ -0,0 +1,7 @@
|
||||
import 'package:readability/readability.dart';
|
||||
|
||||
void main() {
|
||||
var parser = ReadabilityParser();
|
||||
Article article = parser.parse('https://www.bbc.com/sport/football/articles/cl7y4z82z2do');
|
||||
print(article.toString());
|
||||
}
|
9
lib/readability.dart
Normal file
9
lib/readability.dart
Normal file
@ -0,0 +1,9 @@
|
||||
/// Support for doing something awesome.
|
||||
///
|
||||
/// More dartdocs go here.
|
||||
library;
|
||||
|
||||
export 'src/readability_parser.dart';
|
||||
export 'src/article.dart';
|
||||
|
||||
// TODO: Export any libraries intended for clients of this package.
|
33
lib/src/article.dart
Normal file
33
lib/src/article.dart
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
class Article {
|
||||
final String? title;
|
||||
final String? author;
|
||||
final int length;
|
||||
final String? excerpt;
|
||||
final String? siteName;
|
||||
final String? imageUrl;
|
||||
final String? faviconUrl;
|
||||
final String? content;
|
||||
final String? textContent;
|
||||
final String? language;
|
||||
final String? publishedTime;
|
||||
|
||||
Article({
|
||||
required this.title,
|
||||
required this.author,
|
||||
required this.length,
|
||||
required this.excerpt,
|
||||
required this.siteName,
|
||||
required this.imageUrl,
|
||||
required this.faviconUrl,
|
||||
required this.content,
|
||||
required this.textContent,
|
||||
required this.language,
|
||||
required this.publishedTime,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Article{title: $title,\n author: $author,\n length: $length,\n excerpt: $excerpt,\n siteName: $siteName,\n imageUrl: $imageUrl,\n faviconUrl: $faviconUrl,\n content: $content,\n textContent: $textContent,\n language: $language,\n publishedTime: $publishedTime}';
|
||||
}
|
||||
}
|
88
lib/src/readability_parser.dart
Normal file
88
lib/src/readability_parser.dart
Normal file
@ -0,0 +1,88 @@
|
||||
import 'dart:ffi' as ffi;
|
||||
import 'dart:ffi';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:readability/src/article.dart';
|
||||
|
||||
final class CArticle extends Struct {
|
||||
external Pointer<Utf8> title;
|
||||
external Pointer<Utf8> author;
|
||||
@Int32()
|
||||
external int length;
|
||||
external Pointer<Utf8> excerpt;
|
||||
external Pointer<Utf8> site_name;
|
||||
external Pointer<Utf8> image_url;
|
||||
external Pointer<Utf8> favicon_url;
|
||||
external Pointer<Utf8> content;
|
||||
external Pointer<Utf8> text_content;
|
||||
external Pointer<Utf8> language;
|
||||
external Pointer<Utf8> published_time;
|
||||
external Pointer<Utf8> err;
|
||||
|
||||
@Int32()
|
||||
external int success;
|
||||
}
|
||||
|
||||
typedef ParseFunc = CArticle Function(Pointer<Utf8> url);
|
||||
typedef ParseDart = CArticle Function(Pointer<Utf8> url);
|
||||
|
||||
typedef FreeArticleFunc = Void Function(CArticle article);
|
||||
typedef FreeArticleDart = void Function(CArticle article);
|
||||
|
||||
class ReadabilityParser {
|
||||
late final DynamicLibrary _lib;
|
||||
late final ParseDart _parse;
|
||||
late final FreeArticleDart _freeArticle;
|
||||
|
||||
ReadabilityParser() {
|
||||
_lib = ffi.DynamicLibrary.open(_getDylibPath());
|
||||
_parse = _lib.lookupFunction<ParseFunc, ParseDart>('Parse');
|
||||
_freeArticle =
|
||||
_lib.lookupFunction<FreeArticleFunc, FreeArticleDart>('FreeArticle');
|
||||
}
|
||||
|
||||
Article parse(String url) {
|
||||
// Convert the URL to a native string.
|
||||
final urlPtr = url.toNativeUtf8();
|
||||
|
||||
// Call the native function.
|
||||
final article = _parse(urlPtr);
|
||||
|
||||
// Free the memory allocated for the URL.
|
||||
malloc.free(urlPtr);
|
||||
|
||||
|
||||
// Convert the native article to a Dart article.
|
||||
final articleDart = Article(
|
||||
title: article.title == nullptr ? null : article.title.toDartString(),
|
||||
author: article.author == nullptr ? null : article.author.toDartString(),
|
||||
length: article.length,
|
||||
excerpt: article.excerpt == nullptr ? null : article.excerpt.toDartString(),
|
||||
siteName: article.site_name == nullptr ? null : article.site_name.toDartString(),
|
||||
imageUrl: article.image_url == nullptr ? null : article.image_url.toDartString(),
|
||||
faviconUrl: article.favicon_url == nullptr ? null : article.favicon_url.toDartString(),
|
||||
content: article.content == nullptr ? null : article.content.toDartString(),
|
||||
textContent: article.text_content == nullptr ? null : article.text_content.toDartString(),
|
||||
language: article.language == nullptr ? null : article.language.toDartString(),
|
||||
publishedTime: article.published_time == nullptr ? null : article.published_time.toDartString(),
|
||||
);
|
||||
|
||||
// Free the memory allocated for the article.
|
||||
_freeArticle(article);
|
||||
|
||||
return articleDart;
|
||||
}
|
||||
}
|
||||
|
||||
String _getDylibPath() {
|
||||
if (Platform.isWindows) {
|
||||
return 'native/windows/x64/readability.dll';
|
||||
} else if (Platform.isMacOS) {
|
||||
return 'native/macos/x64/readability.dylib';
|
||||
} else if (Platform.isLinux) {
|
||||
return 'native/linux/x86-64/libreadability.so';
|
||||
} else {
|
||||
throw UnsupportedError('This platform is not supported.');
|
||||
}
|
||||
}
|
103
native/linux/x86-64/libreadability.h
Normal file
103
native/linux/x86-64/libreadability.h
Normal file
@ -0,0 +1,103 @@
|
||||
/* Code generated by cmd/cgo; DO NOT EDIT. */
|
||||
|
||||
/* package command-line-arguments */
|
||||
|
||||
|
||||
#line 1 "cgo-builtin-export-prolog"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef GO_CGO_EXPORT_PROLOGUE_H
|
||||
#define GO_CGO_EXPORT_PROLOGUE_H
|
||||
|
||||
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
||||
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* Start of preamble from import "C" comments. */
|
||||
|
||||
|
||||
#line 3 "main.go"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
char* title;
|
||||
char* author;
|
||||
int length;
|
||||
char* excerpt;
|
||||
char* site_name;
|
||||
char* image_url;
|
||||
char* favicon_url;
|
||||
char* content; // HTML content
|
||||
char* text_content; // text content
|
||||
char* language;
|
||||
char* published_time;
|
||||
char* err;
|
||||
int success;
|
||||
} CArticle;
|
||||
|
||||
#line 1 "cgo-generated-wrapper"
|
||||
|
||||
|
||||
/* End of preamble from import "C" comments. */
|
||||
|
||||
|
||||
/* Start of boilerplate cgo prologue. */
|
||||
#line 1 "cgo-gcc-export-header-prolog"
|
||||
|
||||
#ifndef GO_CGO_PROLOGUE_H
|
||||
#define GO_CGO_PROLOGUE_H
|
||||
|
||||
typedef signed char GoInt8;
|
||||
typedef unsigned char GoUint8;
|
||||
typedef short GoInt16;
|
||||
typedef unsigned short GoUint16;
|
||||
typedef int GoInt32;
|
||||
typedef unsigned int GoUint32;
|
||||
typedef long long GoInt64;
|
||||
typedef unsigned long long GoUint64;
|
||||
typedef GoInt64 GoInt;
|
||||
typedef GoUint64 GoUint;
|
||||
typedef size_t GoUintptr;
|
||||
typedef float GoFloat32;
|
||||
typedef double GoFloat64;
|
||||
#ifdef _MSC_VER
|
||||
#include <complex.h>
|
||||
typedef _Fcomplex GoComplex64;
|
||||
typedef _Dcomplex GoComplex128;
|
||||
#else
|
||||
typedef float _Complex GoComplex64;
|
||||
typedef double _Complex GoComplex128;
|
||||
#endif
|
||||
|
||||
/*
|
||||
static assertion to make sure the file is being used on architecture
|
||||
at least with matching size of GoInt.
|
||||
*/
|
||||
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
|
||||
|
||||
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
||||
typedef _GoString_ GoString;
|
||||
#endif
|
||||
typedef void *GoMap;
|
||||
typedef void *GoChan;
|
||||
typedef struct { void *t; void *v; } GoInterface;
|
||||
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
|
||||
|
||||
#endif
|
||||
|
||||
/* End of boilerplate cgo prologue. */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern CArticle Parse(char* url);
|
||||
extern void FreeArticle(CArticle article);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
BIN
native/linux/x86-64/libreadability.so
Normal file
BIN
native/linux/x86-64/libreadability.so
Normal file
Binary file not shown.
16
pubspec.yaml
Normal file
16
pubspec.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
name: readability
|
||||
description: A starting point for Dart libraries or applications.
|
||||
version: 1.0.0
|
||||
# repository: https://github.com/my_org/my_repo
|
||||
|
||||
environment:
|
||||
sdk: ^3.4.3
|
||||
|
||||
# Add regular dependencies here.
|
||||
dependencies:
|
||||
ffi: ^2.1.2
|
||||
# path: ^1.8.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^3.0.0
|
||||
test: ^1.24.0
|
17
test/readability_test.dart
Normal file
17
test/readability_test.dart
Normal file
@ -0,0 +1,17 @@
|
||||
import 'package:readability/readability.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('A group of tests', () {
|
||||
final ReadabilityParser parser = ReadabilityParser();
|
||||
|
||||
setUp(() {
|
||||
// Additional setup goes here.
|
||||
});
|
||||
|
||||
test('First Test', () {
|
||||
final Article article = parser.parse('https://www.bbc.com/sport/football/articles/cl7y4z82z2do');
|
||||
expect(article.title, isNotNull);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user