feat: set up FFI

Signed-off-by: Sphericalkat <me@kat.bio>
This commit is contained in:
Amogh Lele 2024-07-17 02:32:33 +05:30
commit a5b8aa83d6
Signed by: sphericalkat
GPG Key ID: 1C022B9CED2425B4
14 changed files with 361 additions and 0 deletions

3
.fvmrc Normal file
View File

@ -0,0 +1,3 @@
{
"flutter": "stable"
}

10
.gitignore vendored Normal file
View 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
View File

@ -0,0 +1,3 @@
{
"dart.flutterSdkPath": ".fvm/versions/stable"
}

3
CHANGELOG.md Normal file
View File

@ -0,0 +1,3 @@
## 1.0.0
- Initial version.

39
README.md Normal file
View 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
View 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

View 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
View 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
View 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}';
}
}

View 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.');
}
}

View 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

Binary file not shown.

16
pubspec.yaml Normal file
View 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

View 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);
});
});
}