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