commit a5b8aa83d6d238d2baff69f37e94b567a23467f8 Author: Sphericalkat Date: Wed Jul 17 02:32:33 2024 +0530 feat: set up FFI Signed-off-by: Sphericalkat diff --git a/.fvmrc b/.fvmrc new file mode 100644 index 0000000..c300356 --- /dev/null +++ b/.fvmrc @@ -0,0 +1,3 @@ +{ + "flutter": "stable" +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fc683f2 --- /dev/null +++ b/.gitignore @@ -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/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1395495 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "dart.flutterSdkPath": ".fvm/versions/stable" +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..effe43c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8b55e73 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ + + +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. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..dee8927 --- /dev/null +++ b/analysis_options.yaml @@ -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 diff --git a/example/readability_example.dart b/example/readability_example.dart new file mode 100644 index 0000000..bfcb707 --- /dev/null +++ b/example/readability_example.dart @@ -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()); +} diff --git a/lib/readability.dart b/lib/readability.dart new file mode 100644 index 0000000..d57c19c --- /dev/null +++ b/lib/readability.dart @@ -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. diff --git a/lib/src/article.dart b/lib/src/article.dart new file mode 100644 index 0000000..5cab916 --- /dev/null +++ b/lib/src/article.dart @@ -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}'; + } +} diff --git a/lib/src/readability_parser.dart b/lib/src/readability_parser.dart new file mode 100644 index 0000000..02cb5b7 --- /dev/null +++ b/lib/src/readability_parser.dart @@ -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 title; + external Pointer author; + @Int32() + external int length; + external Pointer excerpt; + external Pointer site_name; + external Pointer image_url; + external Pointer favicon_url; + external Pointer content; + external Pointer text_content; + external Pointer language; + external Pointer published_time; + external Pointer err; + + @Int32() + external int success; +} + +typedef ParseFunc = CArticle Function(Pointer url); +typedef ParseDart = CArticle Function(Pointer 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('Parse'); + _freeArticle = + _lib.lookupFunction('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.'); + } +} diff --git a/native/linux/x86-64/libreadability.h b/native/linux/x86-64/libreadability.h new file mode 100644 index 0000000..59a13eb --- /dev/null +++ b/native/linux/x86-64/libreadability.h @@ -0,0 +1,103 @@ +/* Code generated by cmd/cgo; DO NOT EDIT. */ + +/* package command-line-arguments */ + + +#line 1 "cgo-builtin-export-prolog" + +#include + +#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 + +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 +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 diff --git a/native/linux/x86-64/libreadability.so b/native/linux/x86-64/libreadability.so new file mode 100644 index 0000000..5c975ca Binary files /dev/null and b/native/linux/x86-64/libreadability.so differ diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..8fb32f8 --- /dev/null +++ b/pubspec.yaml @@ -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 diff --git a/test/readability_test.dart b/test/readability_test.dart new file mode 100644 index 0000000..0a627de --- /dev/null +++ b/test/readability_test.dart @@ -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); + }); + }); +}