/*
* Copyright (c) 2024-2025 Fabian Freund.
*
* This file is part of WebLibre
* (see https://weblibre.eu).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:drift/drift.dart' show Expression, Insertable, Value;
import 'package:fast_equatable/fast_equatable.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:weblibre/features/bangs/data/database/database.dart'
show BangCompanion;
import 'package:weblibre/features/bangs/data/models/bang_group.dart';
part 'bang.g.dart';
enum BangFormat {
///When the bang is invoked with no query, opens the base path of the URL (/)
///instead of any path given in the template (g., /search)
@JsonValue('open_base_path')
openBasePath,
///URL encode the search terms. Some sites do not work with this, so it can
///be disabled by omitting this.
@JsonValue('url_encode_placeholder')
urlEncodePlaceholder,
///URL encodes spaces as +, instead of %20. Some sites only work correctly
///with one or the other.
@JsonValue('url_encode_space_to_plus')
urlEncodeSpaceToPlus,
}
@JsonSerializable()
@CopyWith()
class Bang with FastEquatable implements Insertable {
static const _templateQueryPlaceholder = '{{{s}}}';
@JsonKey(includeFromJson: false, includeToJson: false)
final BangGroup? group;
///The name of the website associated with the bang.
@JsonKey(name: 's')
final String websiteName;
///The domain name of the websit
@JsonKey(name: 'd')
final String domain;
///The specific trigger word or phrase used to invoke the bang.
@JsonKey(name: 't')
final String trigger;
///The URL template to use when the bang is invoked, where `{{{s}}}` is replaced by the user's query.
@JsonKey(name: 'u')
final String urlTemplate;
///The category of the website, if applicable
@JsonKey(name: 'c')
final String? category;
///The subcategory of the website, if applicable
@JsonKey(name: 'sc')
final String? subCategory;
///The format flags indicating how the query should be processed.
@JsonKey(name: 'fmt')
final Set? format;
String formatQuery(String input) {
return (format == null ||
format!.contains(BangFormat.urlEncodePlaceholder) == true)
? (format == null ||
format?.contains(BangFormat.urlEncodeSpaceToPlus) == true)
? Uri.encodeQueryComponent(input)
: Uri.encodeComponent(input)
: input;
}
Uri getTemplateUrl(String? query) {
final url = (query != null)
? urlTemplate.replaceAll(_templateQueryPlaceholder, formatQuery(query))
: urlTemplate;
var template = Uri.parse(url);
if (!template.hasScheme || template.origin.isEmpty) {
template = Uri.https(
domain,
).replace(path: template.path, query: template.query);
}
return template;
}
static Set decodeFormat(Iterable input) {
return input.map((e) => $enumDecode(_$BangFormatEnumMap, e)).toSet();
}
static List encodeFormat(Iterable format) {
return format.map((e) => _$BangFormatEnumMap[e]!).toList();
}
Bang({
required this.websiteName,
required this.domain,
required this.trigger,
required this.urlTemplate,
this.group,
this.category,
this.subCategory,
this.format,
});
factory Bang.fromJson(Map json) => _$BangFromJson(json);
Map toJson() => _$BangToJson(this);
@override
List