mirror of
https://github.com/lifegpc/pixiv_downloader.git
synced 2026-06-06 05:49:01 +08:00
add avdict
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -441,6 +441,12 @@ dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flagset"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda653ca797810c02f7ca4b804b40b8b95ae046eb989d356bce17919a8c25499"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.22"
|
||||
@@ -1096,6 +1102,7 @@ dependencies = [
|
||||
"cmake",
|
||||
"dateparser",
|
||||
"derive_more",
|
||||
"flagset",
|
||||
"futures-util",
|
||||
"getopts",
|
||||
"gettext",
|
||||
|
||||
@@ -10,6 +10,7 @@ c_fixed_string = { version = "0.2", optional = true }
|
||||
chrono = "0.4"
|
||||
dateparser = "0.1.6"
|
||||
derive_more = "0.99"
|
||||
flagset = { version = "0.4", optional = true }
|
||||
futures-util = "0.3"
|
||||
getopts = "0.2"
|
||||
gettext = "0.4"
|
||||
@@ -30,7 +31,9 @@ bindgen = { version = "0.59", optional = true }
|
||||
cmake = { version = "0.1", optional = true }
|
||||
|
||||
[features]
|
||||
avdict = ["bindgen", "cmake", "flagset"]
|
||||
exif = ["bindgen", "c_fixed_string", "cmake", "link-cplusplus", "int-enum", "utf16string"]
|
||||
ugoira = ["avdict", "bindgen", "cmake"]
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3.9", features = ["winnls", "stringapiset"] }
|
||||
|
||||
1
avdict/.gitignore
vendored
Normal file
1
avdict/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
build/
|
||||
23
avdict/CMakeLists.txt
Normal file
23
avdict/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
cmake_minimum_required(VERSION 3.9)
|
||||
|
||||
project(avdict)
|
||||
|
||||
if (MSVC)
|
||||
add_compile_options(/utf-8)
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake")
|
||||
include(GetLinkLibraries)
|
||||
find_package(AVUTIL REQUIRED)
|
||||
|
||||
add_library(avdict STATIC avdict.h src/avdict.c)
|
||||
target_link_libraries(avdict AVUTIL::AVUTIL)
|
||||
target_compile_definitions(avdict PRIVATE -DBUILD_AVDICT)
|
||||
|
||||
get_link_libraries(OUT avdict)
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/avdict_dep.txt" "${OUT}")
|
||||
|
||||
install(TARGETS avdict)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/avdict_dep.txt" DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
30
avdict/avdict.h
Normal file
30
avdict/avdict.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef _AVDICT_H
|
||||
#define _AVDICT_H
|
||||
#include <stdint.h>
|
||||
#ifndef BUILD_AVDICT
|
||||
#define AV_DICT_MATCH_CASE 1
|
||||
#define AV_DICT_IGNORE_SUFFIX 2
|
||||
#define AV_DICT_DONT_STRDUP_KEY 4
|
||||
#define AV_DICT_DONT_STRDUP_VAL 8
|
||||
#define AV_DICT_DONT_OVERWRITE 16
|
||||
#define AV_DICT_APPEND 32
|
||||
#define AV_DICT_MULTIKEY 64
|
||||
#endif
|
||||
/// <div rustbindgen opaque></div>
|
||||
typedef struct AVDict AVDict;
|
||||
typedef struct AVDictEntry {
|
||||
char* key;
|
||||
char* value;
|
||||
} AVDictEntry;
|
||||
AVDictEntry* avdict_get(const AVDict* m, const char* key, const AVDictEntry* prev, int flags);
|
||||
int avdict_count(const AVDict* m);
|
||||
int avdict_set(AVDict** pm, const char* key, const char* value, int flags);
|
||||
int avdict_copy(AVDict** dst, const AVDict* src, int flags);
|
||||
void avdict_free(AVDict** m);
|
||||
void avdict_mfree(void* data);
|
||||
char* avdict_get_errmsg(int code);
|
||||
int avdict_set_int(AVDict** pm, const char* key, int64_t value, int flags);
|
||||
int avdict_parse_string(AVDict** pm, const char* str, const char* key_val_sep, const char* pairs_sep, int flags);
|
||||
void avdict_avfree(void* data);
|
||||
int avdict_get_string(const AVDict* m, char** buffer, const char key_val_sep, const char pairs_sep);
|
||||
#endif
|
||||
58
avdict/src/avdict.c
Normal file
58
avdict/src/avdict.c
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "../avdict.h"
|
||||
#include "libavutil/avutil.h"
|
||||
#include "libavutil/dict.h"
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
struct AVDictionary {
|
||||
int count;
|
||||
AVDictionaryEntry* elems;
|
||||
};
|
||||
|
||||
AVDictEntry* avdict_get(const AVDict* m, const char* key, const AVDictEntry* prev, int flags) {
|
||||
return (AVDictEntry*)av_dict_get((const AVDictionary*)m, key, (const AVDictionaryEntry*)prev, flags);
|
||||
}
|
||||
|
||||
int avdict_count(const AVDict* m) {
|
||||
return av_dict_count((const AVDictionary*)m);
|
||||
}
|
||||
|
||||
int avdict_set(AVDict** pm, const char* key, const char* value, int flags) {
|
||||
return av_dict_set((AVDictionary**)pm, key, value, flags);
|
||||
}
|
||||
|
||||
int avdict_copy(AVDict** dst, const AVDict* src, int flags) {
|
||||
return av_dict_copy((AVDictionary**)dst, (const AVDictionary*)src, flags);
|
||||
}
|
||||
|
||||
void avdict_free(AVDict** m) {
|
||||
av_dict_free((AVDictionary**)m);
|
||||
}
|
||||
|
||||
void avdict_mfree(void* data) {
|
||||
if (!data) return;
|
||||
free(data);
|
||||
}
|
||||
|
||||
char* avdict_get_errmsg(int code) {
|
||||
char* msg = malloc(AV_ERROR_MAX_STRING_SIZE);
|
||||
if (!msg) return NULL;
|
||||
return av_make_error_string(msg, AV_ERROR_MAX_STRING_SIZE, code);
|
||||
}
|
||||
|
||||
int avdict_set_int(AVDict** pm, const char* key, int64_t value, int flags) {
|
||||
return av_dict_set_int((AVDictionary**)pm, key, value, flags);
|
||||
}
|
||||
|
||||
int avdict_parse_string(AVDict** pm, const char* str, const char* key_val_sep, const char* pairs_sep, int flags) {
|
||||
return av_dict_parse_string((AVDictionary**)pm, str, key_val_sep, pairs_sep, flags);
|
||||
}
|
||||
|
||||
void avdict_avfree(void* data) {
|
||||
if (!data) return;
|
||||
av_free(data);
|
||||
}
|
||||
|
||||
int avdict_get_string(const AVDict* m, char** buffer, const char key_val_sep, const char pairs_sep) {
|
||||
return av_dict_get_string((const AVDictionary*)m, buffer, key_val_sep, pairs_sep);
|
||||
}
|
||||
56
build.rs
56
build.rs
@@ -3,15 +3,15 @@ extern crate bindgen;
|
||||
#[cfg(feature = "cmake")]
|
||||
extern crate cmake;
|
||||
|
||||
#[cfg(any(feature = "exif"))]
|
||||
#[cfg(any(feature = "avdict", feature = "exif"))]
|
||||
use std::env;
|
||||
#[cfg(any(feature = "exif"))]
|
||||
#[cfg(any(feature = "avdict", feature = "exif"))]
|
||||
use std::fs::create_dir;
|
||||
#[cfg(any(feature = "exif"))]
|
||||
#[cfg(any(feature = "avdict", feature = "exif"))]
|
||||
use std::fs::File;
|
||||
#[cfg(any(feature = "exif"))]
|
||||
#[cfg(any(feature = "avdict", feature = "exif"))]
|
||||
use std::io::Read;
|
||||
#[cfg(any(feature = "exif"))]
|
||||
#[cfg(any(feature = "avdict", feature = "exif"))]
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
@@ -51,6 +51,52 @@ fn main() {
|
||||
}
|
||||
println!("cargo:rerun-if-changed=utils/");
|
||||
}
|
||||
#[cfg(feature = "avdict")]
|
||||
{
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
let avdict_build_path = out_path.join("avdict");
|
||||
if !avdict_build_path.exists() {
|
||||
create_dir(&avdict_build_path).unwrap();
|
||||
}
|
||||
cmake::Config::new("avdict")
|
||||
.define("CMAKE_INSTALL_PREFIX", out_path.to_str().unwrap())
|
||||
.out_dir(avdict_build_path)
|
||||
.define("CMAKE_BUILD_TYPE", "Release")
|
||||
.build();
|
||||
println!("cargo:rustc-link-search=native={}/lib", out_path.display());
|
||||
let dep_path = out_path.join("avdict_dep.txt");
|
||||
let mut f = File::open(dep_path).unwrap();
|
||||
let mut s = String::from("");
|
||||
f.read_to_string(&mut s).unwrap();
|
||||
println!("cargo:rustc-link-lib=static=avdict");
|
||||
let l: Vec<&str> = s.split(";").collect();
|
||||
for i in l.iter() {
|
||||
let mut p = PathBuf::from(i);
|
||||
let p2 = p.clone();
|
||||
let file_name = p2.file_stem().unwrap();
|
||||
let file_name = file_name.to_str().unwrap();
|
||||
let file_name = file_name.trim_start_matches("lib");
|
||||
p.pop();
|
||||
println!("cargo:rustc-link-search={}", p.to_str().unwrap());
|
||||
println!("cargo:rustc-link-lib={}", file_name);
|
||||
}
|
||||
println!("cargo:rerun-if-changed=avdict/");
|
||||
let bindings = bindgen::Builder::default()
|
||||
// The input header we would like to generate
|
||||
// bindings for.
|
||||
.header("avdict/avdict.h")
|
||||
// Tell cargo to invalidate the built crate whenever any of the
|
||||
// included header files changed.
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
|
||||
// Finish the builder and generate the bindings.
|
||||
.generate()
|
||||
// Unwrap the Result and panic on failure.
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
bindings
|
||||
.write_to_file(out_path.join("avdict.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
}
|
||||
#[cfg(feature = "exif")]
|
||||
{
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
|
||||
29
cmake/FindAVUTIL.cmake
Normal file
29
cmake/FindAVUTIL.cmake
Normal file
@@ -0,0 +1,29 @@
|
||||
find_package(PkgConfig)
|
||||
if (PkgConfig_FOUND)
|
||||
pkg_check_modules(PC_AVUTIL QUIET IMPORTED_TARGET GLOBAL libavutil)
|
||||
endif()
|
||||
|
||||
if (PC_AVUTIL_FOUND)
|
||||
set(AVUTIL_FOUND TRUE)
|
||||
set(AVUTIL_VERSION ${PC_AVUTIL_VERSION})
|
||||
set(AVUTIL_VERSION_STRING ${PC_AVUTIL_STRING})
|
||||
set(AVUTIL_LIBRARYS ${PC_AVUTIL_LIBRARIES})
|
||||
if (USE_STATIC_LIBS)
|
||||
set(AVUTIL_INCLUDE_DIRS ${PC_AVUTIL_STATIC_INCLUDE_DIRS})
|
||||
else()
|
||||
set(AVUTIL_INCLUDE_DIRS ${PC_AVUTIL_INCLUDE_DIRS})
|
||||
endif()
|
||||
if (NOT TARGET AVUTIL::AVUTIL)
|
||||
add_library(AVUTIL::AVUTIL ALIAS PkgConfig::PC_AVUTIL)
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "failed to find libavutil.")
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(AVUTIL
|
||||
FOUND_VAR AVUTIL_FOUND
|
||||
REQUIRED_VARS
|
||||
AVUTIL_LIBRARYS
|
||||
VERSION_VAR AVUTIL_VERSION
|
||||
)
|
||||
31
cmake/GetLinkLibraries.cmake
Normal file
31
cmake/GetLinkLibraries.cmake
Normal file
@@ -0,0 +1,31 @@
|
||||
function(get_link_libraries OUTPUT_LIST TARGET)
|
||||
get_target_property(IMPORTED ${TARGET} IMPORTED)
|
||||
list(APPEND VISITED_TARGETS ${TARGET})
|
||||
if (IMPORTED)
|
||||
get_target_property(LIBS ${TARGET} INTERFACE_LINK_LIBRARIES)
|
||||
else()
|
||||
get_target_property(LIBS ${TARGET} LINK_LIBRARIES)
|
||||
endif()
|
||||
set(LIB_FILES "")
|
||||
foreach(LIB ${LIBS})
|
||||
if (TARGET ${LIB})
|
||||
list(FIND VISITED_TARGETS ${LIB} VISITED)
|
||||
if (${VISITED} EQUAL -1)
|
||||
get_target_property(LIB_FILE ${LIB} LOCATION)
|
||||
if (NOT LIB_FILE)
|
||||
get_target_property(LIB_FILE ${LIB} IMPORTED_LOCATION)
|
||||
endif()
|
||||
if (NOT LIB_FILE)
|
||||
get_target_property(LIB_FILE ${LIB} IMPORTED_IMPLIB)
|
||||
endif()
|
||||
if (NOT LIB_FILE)
|
||||
get_target_property(LIB_FILE ${LIB} INTERFACE_LINK_LIBRARIES)
|
||||
endif()
|
||||
get_link_libraries(LINK_LIB_FILES ${LIB})
|
||||
list(APPEND LIB_FILES ${LIB_FILE} ${LINK_LIB_FILES})
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
set(VISITED_TARGETS ${VISITED_TARGETS} PARENT_SCOPE)
|
||||
set(${OUTPUT_LIST} ${LIB_FILES} PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -20,8 +20,8 @@ endif()
|
||||
|
||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../utils")
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
find_package(Exiv2)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake")
|
||||
find_package(Exiv2 REQUIRED)
|
||||
|
||||
include_directories(${Exiv2_INCLUDE_DIRS})
|
||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
6
src/_avdict.rs
Normal file
6
src/_avdict.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(deref_nullptr)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
include!(concat!(env!("OUT_DIR"), "/avdict.rs"));
|
||||
389
src/avdict.rs
Normal file
389
src/avdict.rs
Normal file
@@ -0,0 +1,389 @@
|
||||
use crate::_avdict;
|
||||
use crate::ext::cstr::{ToCStr, ToCStrError};
|
||||
use crate::ext::flagset::ToFlagSet;
|
||||
use crate::ext::rawhandle::ToRawHandle;
|
||||
use crate::gettext;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::AsMut;
|
||||
use std::convert::AsRef;
|
||||
use std::convert::TryFrom;
|
||||
use std::default::Default;
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::CString;
|
||||
use std::fmt::Display;
|
||||
use std::iter::Iterator;
|
||||
use std::ops::Drop;
|
||||
use std::os::raw::c_char;
|
||||
use std::os::raw::c_int;
|
||||
use std::str::Utf8Error;
|
||||
|
||||
#[derive(Debug, derive_more::From, PartialEq)]
|
||||
pub enum AVDictError {
|
||||
String(String),
|
||||
Utf8Error(Utf8Error),
|
||||
CodeError(AVDictCodeError),
|
||||
ToCstr(ToCStrError)
|
||||
}
|
||||
|
||||
impl From<&str> for AVDictError {
|
||||
fn from(s: &str) -> Self {
|
||||
Self::String(String::from(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<c_int> for AVDictError {
|
||||
fn from(i: c_int) -> Self {
|
||||
Self::CodeError(AVDictCodeError::from(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AVDictError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::String(s) => { f.write_str(s) }
|
||||
Self::Utf8Error(s) => { f.write_fmt(format_args!("{} {}", gettext("Failed to decode string with UTF-8:"), s)) }
|
||||
Self::CodeError(s) => { s.fmt(f) }
|
||||
Self::ToCstr(s) => { s.fmt(f) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
||||
pub struct AVDictCodeError {
|
||||
err: c_int,
|
||||
}
|
||||
|
||||
impl AVDictCodeError {
|
||||
pub fn to_str(&self) -> Result<String, AVDictError> {
|
||||
let s = unsafe { _avdict::avdict_get_errmsg(self.err) };
|
||||
if s.is_null() {
|
||||
Err(gettext("Out of memory."))?;
|
||||
}
|
||||
let ss = unsafe { CStr::from_ptr(s) };
|
||||
let ss = ss.to_owned();
|
||||
unsafe { _avdict::avdict_mfree(s as *mut std::os::raw::c_void) };
|
||||
let re = ss.to_str()?;
|
||||
Ok(String::from(re))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AVDictCodeError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.to_str() {
|
||||
Ok(s) => {
|
||||
f.write_str(s.as_str())
|
||||
}
|
||||
Err(e) => {
|
||||
f.write_fmt(format_args!("{} {}", gettext("Failed to get error message:"), e))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<c_int> for AVDictCodeError {
|
||||
fn from(i: c_int) -> Self {
|
||||
Self {
|
||||
err: i,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flagset::flags! {
|
||||
pub enum AVDictFlags: c_int {
|
||||
/// Only get an entry with exact-case key match.
|
||||
MatchCase = _avdict::AV_DICT_MATCH_CASE as c_int,
|
||||
/// Return first entry in a dictionary whose first part corresponds to the search key,
|
||||
/// ignoring the suffix of the found key string.
|
||||
IgnoreSuffix = _avdict::AV_DICT_IGNORE_SUFFIX as c_int,
|
||||
/// Take ownership of a key that's been allocated
|
||||
DontStrdupKey = _avdict::AV_DICT_DONT_STRDUP_KEY as c_int,
|
||||
/// Take ownership of a value that's been allocated
|
||||
DontStrdupVal = _avdict::AV_DICT_DONT_STRDUP_VAL as c_int,
|
||||
/// Don't overwrite existing entries.
|
||||
DontOverwrite = _avdict::AV_DICT_DONT_OVERWRITE as c_int,
|
||||
/// If the entry already exists, append to it.
|
||||
Append = _avdict::AV_DICT_APPEND as c_int,
|
||||
/// Allow to store several equal keys in the dictionary.
|
||||
Multikey = _avdict::AV_DICT_MULTIKEY as c_int,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AVDict {
|
||||
m: *mut _avdict::AVDict,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl AVDict {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
m: 0 as *mut _avdict::AVDict,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy<T: ToFlagSet<AVDictFlags>>(&self, flags: T) -> Result<Self, AVDictError> {
|
||||
if self.m.is_null() {
|
||||
return Ok(Self::new());
|
||||
}
|
||||
let mut m = 0 as *mut _avdict::AVDict;
|
||||
let pm: *mut *mut _avdict::AVDict = &mut m;
|
||||
let re = unsafe { _avdict::avdict_copy(pm, self.m, flags.to_bits()) };
|
||||
if re != 0 {
|
||||
Err(re)?;
|
||||
}
|
||||
Ok(Self {
|
||||
m,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_map<K: ToCStr, V: ToCStr, F: ToFlagSet<AVDictFlags>>(&mut self, maps: &HashMap<K, V>, flags: F) -> Result<(), AVDictError> {
|
||||
let flags = flags.to_flag_set();
|
||||
for (k, v) in maps {
|
||||
self.set(k, v, flags)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get<K: ToCStr, F: ToFlagSet<AVDictFlags>>(&self, key: K, flags: F) -> Result<Option<CString>, AVDictError> {
|
||||
if self.m.is_null() {
|
||||
return Ok(None);
|
||||
}
|
||||
let k = key.to_cstr()?;
|
||||
let re = unsafe { _avdict::avdict_get(self.m, k.as_ptr(), 0 as *mut _avdict::AVDictEntry, flags.to_bits()) };
|
||||
if !re.is_null() && unsafe { !(*re).value.is_null() } {
|
||||
let s = unsafe { CStr::from_ptr((*re).value) };
|
||||
let s = s.to_owned();
|
||||
return Ok(Some(s));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn get_all<K: ToCStr, F: ToFlagSet<AVDictFlags>>(&self, key: K, flags: F) -> Result<Option<Vec<CString>>, AVDictError> {
|
||||
if self.m.is_null() {
|
||||
return Ok(None);
|
||||
}
|
||||
let k = key.to_cstr()?;
|
||||
let mut re = unsafe { _avdict::avdict_get(self.m, k.as_ptr(), 0 as *mut _avdict::AVDictEntry, flags.to_bits()) };
|
||||
let mut l = Vec::new();
|
||||
while !re.is_null() {
|
||||
if unsafe { (*re).value.is_null() } {
|
||||
Err(gettext("Failed to get value for entry."))?;
|
||||
}
|
||||
let s = unsafe { CStr::from_ptr((*re).value) };
|
||||
let s = s.to_owned();
|
||||
l.push(s);
|
||||
re = unsafe { _avdict::avdict_get(self.m, k.as_ptr(), re, flags.to_bits()) };
|
||||
}
|
||||
if l.len() > 0 {
|
||||
return Ok(Some(l));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Get dictionary entries as a string.
|
||||
///
|
||||
/// Return a string containing dictionary's entries.
|
||||
/// * `key_val_sep` - character used to separate key from value
|
||||
/// * `pairs_sep` - character used to separate two pairs from each other
|
||||
/// # Note
|
||||
/// String is escaped with backslashes (`\`).
|
||||
/// # Warning
|
||||
/// Separators cannot be neither `\` nor `\0`. They also cannot be the same.
|
||||
pub fn get_string(&self, key_val_sep: char, pairs_sep: char) -> Result<CString, AVDictError> {
|
||||
let mut buf = 0 as *mut c_char;
|
||||
let pbuf: *mut *mut c_char = &mut buf;
|
||||
let re = unsafe { _avdict::avdict_get_string(self.m, pbuf, key_val_sep as c_char, pairs_sep as c_char) };
|
||||
if re != 0 {
|
||||
Err(re)?;
|
||||
}
|
||||
let s = unsafe { CStr::from_ptr(buf) };
|
||||
let s = s.to_owned();
|
||||
unsafe { _avdict::avdict_avfree(buf as *mut std::os::raw::c_void) };
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
pub fn iter<'a>(&'a self) -> AVDictItor<'a> {
|
||||
AVDictItor {
|
||||
d: self,
|
||||
cur: 0 as *mut _avdict::AVDictEntry,
|
||||
started: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
if self.m.is_null() {
|
||||
0
|
||||
} else {
|
||||
unsafe { _avdict::avdict_count(self.m) as usize }
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse the key/value pairs list and add the parsed entries to a dictionary.
|
||||
///
|
||||
/// In case of failure, all the successfully set entries are stored.
|
||||
/// * `s` - string
|
||||
/// * `key_val_sep` - a list of characters used to separate key from value
|
||||
/// * `pairs_sep` - a list of characters used to separate two pairs from each other
|
||||
/// * `flags` - flags to use when adding to dictionary.
|
||||
/// StrdupKey and StrdipValue are ignored since the key/value tokens will always be duplicated.
|
||||
pub fn parse_string<K: ToCStr, V: ToCStr, S: ToCStr, F: ToFlagSet<AVDictFlags>>(&mut self, s: K, key_val_sep: V, pairs_sep: S, flags: F) -> Result<(), AVDictError> {
|
||||
let pm: *mut *mut _avdict::AVDict = &mut self.m;
|
||||
let s = s.to_cstr()?;
|
||||
let k = key_val_sep.to_cstr()?;
|
||||
let p = pairs_sep.to_cstr()?;
|
||||
let re = unsafe { _avdict::avdict_parse_string(pm, s.as_ptr(), k.as_ptr(), p.as_ptr(), flags.to_bits()) };
|
||||
if re != 0 {
|
||||
Err(re)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set<K: ToCStr, V: ToCStr, F: ToFlagSet<AVDictFlags>>(&mut self, key: K, value: V, flags: F) -> Result<(), AVDictError> {
|
||||
let pm: *mut *mut _avdict::AVDict = &mut self.m;
|
||||
let k = key.to_cstr()?;
|
||||
let v = value.to_cstr()?;
|
||||
let re = unsafe { _avdict::avdict_set(pm, k.as_ptr(), v.as_ptr(), flags.to_bits()) };
|
||||
if re != 0 {
|
||||
Err(re)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_int<K: ToCStr, F: ToFlagSet<AVDictFlags>>(&mut self, key: K, value: i64, flags: F) -> Result<(), AVDictError> {
|
||||
let pm: *mut *mut _avdict::AVDict = &mut self.m;
|
||||
let k = key.to_cstr()?;
|
||||
let re = unsafe { _avdict::avdict_set_int(pm, k.as_ptr(), value, flags.to_bits()) };
|
||||
if re != 0 {
|
||||
Err(re)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<Self> for AVDict {
|
||||
fn as_mut(&mut self) -> &mut Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Self> for AVDict {
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AVDict {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AVDict {
|
||||
fn drop(&mut self) {
|
||||
if !self.m.is_null() {
|
||||
let ptr: *mut *mut _avdict::AVDict = &mut self.m;
|
||||
unsafe { _avdict::avdict_free(ptr) };
|
||||
self.m = 0 as *mut _avdict::AVDict;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <K: ToCStr, V: ToCStr> TryFrom<HashMap<K, V>> for AVDict {
|
||||
type Error = AVDictError;
|
||||
fn try_from(value: HashMap<K, V>) -> Result<Self, Self::Error> {
|
||||
Self::try_from(&value)
|
||||
}
|
||||
}
|
||||
|
||||
impl <K: ToCStr, V: ToCStr> TryFrom<&HashMap<K, V>> for AVDict {
|
||||
type Error = AVDictError;
|
||||
fn try_from(value: &HashMap<K, V>) -> Result<Self, Self::Error> {
|
||||
let mut d = Self::new();
|
||||
for (k, v) in value {
|
||||
d.set(k, v, AVDictFlags::MatchCase | AVDictFlags::Multikey)?;
|
||||
}
|
||||
Ok(d)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToRawHandle<_avdict::AVDict> for AVDict {
|
||||
unsafe fn to_raw_handle(&self) -> *mut _avdict::AVDict {
|
||||
self.m
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AVDictItor<'a> {
|
||||
d: &'a AVDict,
|
||||
cur: *mut _avdict::AVDictEntry,
|
||||
started: bool,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
#[doc(hidden)]
|
||||
static ref NULLSTR: CString = CString::new("").unwrap();
|
||||
}
|
||||
|
||||
impl<'a> Iterator for AVDictItor<'a> {
|
||||
type Item = (CString, CString);
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.started && self.cur.is_null() {
|
||||
return None;
|
||||
}
|
||||
self.started = true;
|
||||
let m = unsafe { self.d.to_raw_handle() };
|
||||
let re = unsafe { _avdict::avdict_get(m, NULLSTR.as_ptr(), self.cur as *const _avdict::AVDictEntry, AVDictFlags::IgnoreSuffix.to_bits()) };
|
||||
self.cur = re;
|
||||
if re.is_null() {
|
||||
return None;
|
||||
}
|
||||
let k = unsafe { CStr::from_ptr((*re).key) };
|
||||
let k = k.to_owned();
|
||||
let v = unsafe { CStr::from_ptr((*re).value) };
|
||||
let v = v.to_owned();
|
||||
Some((k, v))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_avdict() {
|
||||
let e = AVDictCodeError::from(-1);
|
||||
assert_eq!(Ok(String::from("Operation not permitted")), e.to_str());
|
||||
let mut d = AVDict::new();
|
||||
assert_eq!(0, d.len());
|
||||
let d2 = d.copy(None).unwrap();
|
||||
assert_eq!(0, d2.len());
|
||||
d.set("a", String::from("s"), None).unwrap();
|
||||
assert_eq!(1, d.len());
|
||||
assert_eq!(0, d2.len());
|
||||
let mut d2 = d.copy(None).unwrap();
|
||||
assert_eq!(1, d2.len());
|
||||
assert_eq!(Ok(None), d.get("f", None));
|
||||
assert_eq!(Ok(Some(CString::new("s").unwrap())), d.get("a", None));
|
||||
d2.set("b", "ok", AVDictFlags::Multikey).unwrap();
|
||||
d2.set("b", "test", AVDictFlags::Multikey).unwrap();
|
||||
assert_eq!(Ok(Some(CString::new("ok").unwrap())), d2.get("b", None));
|
||||
assert_eq!(Ok(Some(vec![
|
||||
CString::new("ok").unwrap(),
|
||||
CString::new("test").unwrap(),
|
||||
])), d2.get_all("b", None));
|
||||
d.set_int("i", 17, None).unwrap();
|
||||
assert_eq!(Ok(Some(CString::new("17").unwrap())), d.get("i", None));
|
||||
d.set("c", "test", AVDictFlags::Append).unwrap();
|
||||
d.set("c", "test2", AVDictFlags::Append).unwrap();
|
||||
assert_eq!(Ok(Some(CString::new("testtest2").unwrap())), d.get("c", None));
|
||||
assert_eq!(3, d.len());
|
||||
assert_eq!(3, d2.len());
|
||||
let mut m = HashMap::new();
|
||||
m.insert("s", "f");
|
||||
m.insert("f", "dd");
|
||||
let mut d3 = AVDict::new();
|
||||
d3.from_map(&m, None).unwrap();
|
||||
assert_eq!(Ok(Some(CString::new("dd").unwrap())), d3.get("f", None));
|
||||
let d3 = AVDict::try_from(&m).unwrap();
|
||||
assert_eq!(Ok(Some(CString::new("dd").unwrap())), d3.get("f", None));
|
||||
let mut d4 = AVDict::new();
|
||||
d4.parse_string("a=b b=c", "=", " ", None).unwrap();
|
||||
assert_eq!(2, d4.len());
|
||||
assert_eq!(Ok(CString::new("a=b b=c").unwrap()), d4.get_string('=', ' '));
|
||||
let mut it = d4.iter();
|
||||
assert_eq!(Some((CString::new("a").unwrap(), CString::new("b").unwrap())), it.next());
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::_exif;
|
||||
use crate::ext::rawhandle::ToRawHandle;
|
||||
use c_fixed_string::CFixedStr;
|
||||
use int_enum::IntEnum;
|
||||
use std::convert::TryFrom;
|
||||
@@ -90,12 +91,6 @@ pub struct ExifKey {
|
||||
key: *mut _exif::ExifKey,
|
||||
}
|
||||
|
||||
/// Return raw pointer of the handle
|
||||
pub trait ToRawHandle<T> {
|
||||
/// Return raw pointer of the handle
|
||||
unsafe fn to_raw_handle(&self) -> *mut T;
|
||||
}
|
||||
|
||||
impl TryFrom<CString> for ExifKey {
|
||||
type Error = ();
|
||||
fn try_from(value: CString) -> Result<Self, Self::Error> {
|
||||
|
||||
75
src/ext/cstr.rs
Normal file
75
src/ext/cstr.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
#[cfg(feature = "c_fixed_string")]
|
||||
use c_fixed_string::CFixedStr;
|
||||
#[cfg(feature = "c_fixed_string")]
|
||||
use c_fixed_string::CFixedString;
|
||||
use std::ffi::NulError;
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::CString;
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Debug, derive_more::From, PartialEq)]
|
||||
pub enum ToCStrError {
|
||||
Null(NulError),
|
||||
}
|
||||
|
||||
impl Display for ToCStrError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Null(e) => { e.fmt(f) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToCStr {
|
||||
fn to_cstr(&self) -> Result<CString, ToCStrError>;
|
||||
}
|
||||
|
||||
impl ToCStr for CString {
|
||||
fn to_cstr(&self) -> Result<CString, ToCStrError> {
|
||||
Ok(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCStr for CStr {
|
||||
fn to_cstr(&self) -> Result<CString, ToCStrError> {
|
||||
Ok(self.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCStr for [u8] {
|
||||
fn to_cstr(&self) -> Result<CString, ToCStrError> {
|
||||
Ok(CString::new(self)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCStr for &str {
|
||||
fn to_cstr(&self) -> Result<CString, ToCStrError> {
|
||||
(*self).as_bytes().to_cstr()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCStr for String {
|
||||
fn to_cstr(&self) -> Result<CString, ToCStrError> {
|
||||
self.as_bytes().to_cstr()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "c_fixed_string")]
|
||||
impl ToCStr for CFixedStr {
|
||||
fn to_cstr(&self) -> Result<CString, ToCStrError> {
|
||||
Ok(self.to_c_str().into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "c_fixed_string")]
|
||||
impl ToCStr for CFixedString {
|
||||
fn to_cstr(&self) -> Result<CString, ToCStrError> {
|
||||
Ok(self.to_c_str().into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ToCStr> ToCStr for &'a T {
|
||||
fn to_cstr(&self) -> Result<CString, ToCStrError> {
|
||||
(*self).to_cstr()
|
||||
}
|
||||
}
|
||||
30
src/ext/flagset.rs
Normal file
30
src/ext/flagset.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use flagset::Flags;
|
||||
use flagset::FlagSet;
|
||||
|
||||
pub trait ToFlagSet<T: Flags> {
|
||||
fn to_flag_set(&self) -> FlagSet<T>;
|
||||
fn to_bits(&self) -> T::Type {
|
||||
self.to_flag_set().bits()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Flags> ToFlagSet<T> for T where FlagSet<T>: From<T> {
|
||||
fn to_flag_set(&self) -> FlagSet<T> {
|
||||
FlagSet::from(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Flags> ToFlagSet<T> for FlagSet<T> {
|
||||
fn to_flag_set(&self) -> FlagSet<T> {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Flags> ToFlagSet<T> for Option<T> where FlagSet<T>: From<T> {
|
||||
fn to_flag_set(&self) -> FlagSet<T> {
|
||||
match self {
|
||||
Some(s) => { FlagSet::from(s.clone()) }
|
||||
None => { FlagSet::default() }
|
||||
}
|
||||
}
|
||||
}
|
||||
5
src/ext/mod.rs
Normal file
5
src/ext/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub mod cstr;
|
||||
#[cfg(feature = "flagset")]
|
||||
pub mod flagset;
|
||||
#[cfg(any(feature = "exif", feature = "avdict"))]
|
||||
pub mod rawhandle;
|
||||
5
src/ext/rawhandle.rs
Normal file
5
src/ext/rawhandle.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
/// Return raw pointer of the handle
|
||||
pub trait ToRawHandle<T> {
|
||||
/// Return raw pointer of the handle
|
||||
unsafe fn to_raw_handle(&self) -> *mut T;
|
||||
}
|
||||
@@ -3,6 +3,8 @@ extern crate c_fixed_string;
|
||||
extern crate chrono;
|
||||
extern crate dateparser;
|
||||
extern crate derive_more;
|
||||
#[cfg(feature = "flagset")]
|
||||
extern crate flagset;
|
||||
extern crate futures_util;
|
||||
extern crate json;
|
||||
#[cfg(feature = "int-enum")]
|
||||
@@ -19,16 +21,23 @@ extern crate urlparse;
|
||||
extern crate utf16string;
|
||||
extern crate xml;
|
||||
|
||||
#[cfg(feature = "avdict")]
|
||||
#[doc(hidden)]
|
||||
mod _avdict;
|
||||
#[cfg(feature = "exif")]
|
||||
#[doc(hidden)]
|
||||
mod _exif;
|
||||
mod author_name_filter;
|
||||
#[cfg(feature = "avdict")]
|
||||
mod avdict;
|
||||
mod cookies;
|
||||
mod data;
|
||||
mod download;
|
||||
mod dur;
|
||||
#[cfg(feature = "exif")]
|
||||
mod exif;
|
||||
/// Used to extend some thirdparty library
|
||||
mod ext;
|
||||
mod i18n;
|
||||
mod list;
|
||||
mod opthelper;
|
||||
|
||||
1
ugoira/.gitignore
vendored
Normal file
1
ugoira/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
build/
|
||||
Reference in New Issue
Block a user