URHG WURKS

This commit is contained in:
philipp 2023-11-05 22:05:39 +01:00
parent bb3fd142c7
commit 0c2fcfcea8
5 changed files with 332 additions and 155 deletions

View File

@ -1,21 +1,88 @@
use log::debug; use log::{debug, error};
use std::sync::Arc; use std::{
borrow::BorrowMut,
fmt,
rc::{Rc, Weak},
sync::{Arc, Mutex},
};
use crate::overview; use crate::overview;
// pub(crate) struct Law { #[derive(Debug)]
// name: String, //ABGB, UrhG pub(crate) struct Law {
// section: Vec<Section>, // § 1, § 2, ... name: String, //ABGB, UrhG
// } header: Vec<Heading>,
}
// impl Law { #[derive(Debug)]
// pub(crate) fn new(name: &str) -> Self { struct Heading {
// Self { name: String, //1. Hauptstück; 3. Theil; ...
// name: name.into(), desc: Option<String>,
// section: Vec::new(), content: HeadingContent, // 1. Theil; 1. Subtheil; ...
// } }
// }
// } #[derive(Debug)]
enum HeadingContent {
Paragraph(Vec<Section>),
Heading(Vec<Box<Heading>>),
}
fn add_from_node(cur: &ClassifierInstance, builder: &LawBuilder) -> Heading {
let children = builder.get_by_parent(&cur.name);
if children.len() > 0 {
let mut ret = Vec::new();
for child in children {
ret.push(Box::new(add_from_node(&child, builder)));
}
Heading {
name: cur.name.clone(),
desc: cur.desc.clone(),
content: HeadingContent::Heading(ret),
}
} else {
Heading {
name: cur.name.clone(),
desc: cur.desc.clone(),
content: HeadingContent::Paragraph(cur.sections.clone()),
}
}
}
impl From<LawBuilder> for Law {
fn from(builder: LawBuilder) -> Self {
let cur: Vec<Classifier> = builder
.classifiers
.clone()
.into_iter()
.filter(|c| c.parent_index.is_none())
.collect();
let mut ret = Vec::new();
for class in cur {
for child in class.instances {
ret.push(add_from_node(&child, &builder));
}
}
Self {
name: builder.name,
header: ret,
}
}
}
fn contains(classifier_name: &str, instance_name: &str) -> bool {
instance_name
.to_lowercase()
.contains(&classifier_name.to_lowercase())
}
fn starts_with_number(_classifier_name: &str, instance_name: &str) -> bool {
match instance_name.trim().as_bytes().get(0) {
Some(c) if c.is_ascii_digit() => true,
_ => false,
}
}
/// Is used to generate a law struct. It's organized mainly by classifier. /// Is used to generate a law struct. It's organized mainly by classifier.
#[derive(Debug)] #[derive(Debug)]
@ -32,37 +99,38 @@ pub(crate) struct LawBuilder {
next_para_header: Option<String>, next_para_header: Option<String>,
} }
fn contains(classifier_name: &str, instance_name: &str) -> bool {
instance_name.contains(classifier_name)
}
fn starts_with_number(_classifier_name: &str, instance_name: &str) -> bool {
match instance_name.trim().as_bytes().get(0) {
Some(c) if c.is_ascii_digit() => true,
_ => false,
}
}
impl LawBuilder { impl LawBuilder {
#[cfg(test)] #[cfg(test)]
pub(crate) fn test(name: &str) -> Self { pub(crate) fn test(name: &str) -> Self {
let mut classifiers = Vec::new(); let mut classifiers = Vec::new();
if name == "UrhG" { //if name == "UrhG" {
let hauptstueck = Classifier::new("Hauptstück", Arc::new(&contains)); // let hauptstueck = Classifier::new("Hauptstück", Arc::new(&contains));
classifiers.push(hauptstueck.clone()); // classifiers.push(hauptstueck.clone());
let mut abschnitt = Classifier::new("Abschnitt", Arc::new(&contains)); // let mut abschnitt = Classifier::new("Abschnitt", Arc::new(&contains));
abschnitt.set_parent(hauptstueck); // abschnitt.set_parent(&hauptstueck);
classifiers.push(abschnitt); // classifiers.push(abschnitt.clone());
let mut numbered_header = // let mut numbered_header =
Classifier::new("Numbered Header", Arc::new(&starts_with_number)); // Classifier::new("Numbered Header", Arc::new(&starts_with_number));
numbered_header.set_parent(abschnitt); // numbered_header.set_parent(&abschnitt);
classifiers.push(numbered_header); // classifiers.push(numbered_header);
} else if name == "no-headers" { //} else if name == "no-headers" {
let mut class = Classifier::new("", Arc::new(&contains)); // let mut class = Classifier::new("", Arc::new(&contains));
class.add_instance(ClassifierInstance::new("")); // class.add_instance(ClassifierInstance::new(""));
classifiers.push(class); // classifiers.push(class);
//} else
if name == "test" {
let h1 = Classifier::new("h1", Arc::new(&contains));
classifiers.push(h1);
let mut h2 = Classifier::new("h2", Arc::new(&contains));
h2.set_parent(0);
classifiers.push(h2);
let mut h3 = Classifier::new("h3", Arc::new(&contains));
h3.set_parent(1);
classifiers.push(h3);
} }
Self { Self {
name: name.into(), name: name.into(),
@ -73,7 +141,7 @@ impl LawBuilder {
} }
/// Creates a new law builder. Adds classifier for known law texts. /// Creates a new law builder. Adds classifier for known law texts.
pub(crate) fn new(name: &str) -> LawBuilder { pub(crate) fn new(name: &str) -> Law {
//TODO: return Law (not LawBuilder) //TODO: return Law (not LawBuilder)
let mut classifiers = Vec::new(); let mut classifiers = Vec::new();
@ -84,12 +152,22 @@ impl LawBuilder {
classifiers.push(hauptstueck.clone()); classifiers.push(hauptstueck.clone());
let mut abschnitt = Classifier::new("Abschnitt", Arc::new(&contains)); let mut abschnitt = Classifier::new("Abschnitt", Arc::new(&contains));
abschnitt.set_parent(hauptstueck); abschnitt.set_parent(0);
classifiers.push(abschnitt);
let mut numbered_header =
Classifier::new("Numbered Header", Arc::new(&starts_with_number));
numbered_header.set_parent(9999);
classifiers.push(numbered_header);
} else if name == "MSchG" {
law_id = Some(10002180);
let abschnitt = Classifier::new("Abschnitt", Arc::new(&contains));
classifiers.push(abschnitt.clone()); classifiers.push(abschnitt.clone());
let mut numbered_header = let mut numbered_header =
Classifier::new("Numbered Header", Arc::new(&starts_with_number)); Classifier::new("Numbered Header", Arc::new(&starts_with_number));
numbered_header.set_parent(abschnitt); numbered_header.set_parent(0);
classifiers.push(numbered_header); classifiers.push(numbered_header);
} }
@ -102,7 +180,7 @@ impl LawBuilder {
overview::parse(law_id.unwrap(), &mut builder).unwrap(); overview::parse(law_id.unwrap(), &mut builder).unwrap();
builder builder.into()
} }
/// Sets a new header. /// Sets a new header.
@ -112,9 +190,44 @@ impl LawBuilder {
.classifiers .classifiers
.iter() .iter()
.position(|class| class.used_for(name)); .position(|class| class.used_for(name));
match classifier_index { match classifier_index {
Some(index) => { Some(index) => {
let class = ClassifierInstance::new(name); let mut class = ClassifierInstance::new(name, index);
if self.classifiers[index]
.parent_index
.is_some_and(|x| x == 9999)
{
if self.classifiers[self.last_header_index.unwrap()]
.parent_index
.is_some_and(|x| x == 9999)
{
class.add_parent(
&self.classifiers[self.classifiers[self.last_header_index.unwrap()]
.instances
.last()
.unwrap()
.parent
.clone()
.unwrap()
.idx]
.instances
.last()
.unwrap(),
)
} else {
class.add_parent(
self.classifiers[self.last_header_index.unwrap()]
.instances
.last()
.unwrap(),
);
}
} else if let Some(parent) = self.classifiers[index].parent_index {
class.add_parent(self.classifiers[parent].instances.last().unwrap());
}
self.classifiers[index].add_instance(class); self.classifiers[index].add_instance(class);
self.last_header_index = Some(index); self.last_header_index = Some(index);
} }
@ -154,21 +267,35 @@ impl LawBuilder {
self.next_para_header = Some(header.into()); self.next_para_header = Some(header.into());
} }
pub(crate) fn toc(&self) { fn get_by_parent(&self, name: &String) -> Vec<ClassifierInstance> {
let mut ret = Vec::new();
for class in &self.classifiers { for class in &self.classifiers {
for inst in &class.instances { for inst in &class.instances {
println!("{}", inst.name); if let Some(parent) = &inst.parent {
if &parent.name == name {
ret.push(inst.clone());
}
}
} }
} }
ret
} }
} }
#[derive(Debug, Clone)] #[derive(Clone)]
pub(crate) struct Section { pub(crate) struct Section {
symb: String, // §"1", §"2", ... symb: String, // §"1", §"2", ...
par_header: Option<String>, par_header: Option<String>,
content: Content, content: Content,
//header: Option<Header>, }
impl fmt::Debug for Section {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let par_header = self.par_header.as_ref().map(String::as_str).unwrap_or("");
write!(f, "{} ({})", self.symb, par_header)
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -197,14 +324,18 @@ pub(crate) struct ClassifierInstance {
name: String, name: String,
desc: Option<String>, desc: Option<String>,
sections: Vec<Section>, sections: Vec<Section>,
parent: Option<Box<ClassifierInstance>>,
idx: usize,
} }
impl ClassifierInstance { impl ClassifierInstance {
fn new(name: &str) -> Self { fn new(name: &str, idx: usize) -> Self {
Self { Self {
name: name.into(), name: name.into(),
desc: None, desc: None,
sections: Vec::new(), sections: Vec::new(),
parent: None,
idx,
} }
} }
@ -215,12 +346,16 @@ impl ClassifierInstance {
fn add_section(&mut self, section: Section) { fn add_section(&mut self, section: Section) {
self.sections.push(section); self.sections.push(section);
} }
fn add_parent(&mut self, parent: &ClassifierInstance) {
self.parent = Some(Box::new(parent.clone()));
}
} }
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct Classifier { pub(crate) struct Classifier {
name: String, // Hauptstück, Theil, Abschnitt, ol name: String, // Hauptstück, Theil, Abschnitt, ol
parent: Option<Box<Classifier>>, parent_index: Option<usize>,
instances: Vec<ClassifierInstance>, instances: Vec<ClassifierInstance>,
used_for_fn: Arc<dyn Fn(&str, &str) -> bool>, used_for_fn: Arc<dyn Fn(&str, &str) -> bool>,
} }
@ -229,7 +364,7 @@ impl std::fmt::Debug for Classifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Classifier") f.debug_struct("Classifier")
.field("name", &self.name) .field("name", &self.name)
.field("parent", &self.parent) .field("parent_index", &self.parent_index)
.field("instances", &self.instances) .field("instances", &self.instances)
.finish() .finish()
} }
@ -239,14 +374,14 @@ impl Classifier {
fn new(name: &str, used_for_fn: Arc<dyn Fn(&str, &str) -> bool>) -> Self { fn new(name: &str, used_for_fn: Arc<dyn Fn(&str, &str) -> bool>) -> Self {
Self { Self {
name: name.into(), name: name.into(),
parent: None, parent_index: None,
instances: Vec::new(), instances: Vec::new(),
used_for_fn, used_for_fn,
} }
} }
fn set_parent(&mut self, parent: Classifier) { fn set_parent(&mut self, parent: usize) {
self.parent = Some(Box::new(parent)); self.parent_index = Some(parent);
} }
fn contains(&self, name: &str) -> bool { fn contains(&self, name: &str) -> bool {
@ -280,46 +415,63 @@ pub(crate) enum Content {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use pretty_assertions::assert_eq;
#[test] #[test]
fn test() { fn atest() {
let mut builder = LawBuilder::test("UrhG"); let mut builder = LawBuilder::test("test");
builder.new_header("h11");
builder.new_par("§11".into(), Content::Text("hi".into()));
builder.new_header("h21");
builder.new_header("h31");
builder.new_par("§211".into(), Content::Text("hi".into()));
builder.new_par("§212".into(), Content::Text("hi".into()));
builder.new_header("h22");
builder.new_par("§22".into(), Content::Text("hi".into()));
builder.new_header("1. Hauptstück"); let law: Law = builder.into();
builder.new_header("2. Abschnitt");
builder.new_par(Content::Text("Mein erster Paragraph".into())); println!("{law:#?}");
assert!(false);
let expected = LawBuilder {
name: "UrhG".into(),
classifiers: vec![
Classifier {
name: "Hauptstück".into(),
parent: None,
instances: vec![ClassifierInstance {
name: "1. Hauptstück".into(),
desc: None,
content: vec![],
}],
},
Classifier {
name: "Abschnitt".into(),
parent: Some(Box::new(Classifier {
name: "Hauptstück".into(),
parent: None,
instances: vec![],
})),
instances: vec![ClassifierInstance {
name: "2. Abschnitt".into(),
desc: None,
content: vec![Content::Text("Mein erster Paragraph".into())],
}],
},
],
next_para_header: None,
last_header_index: Some(1),
};
assert_eq!(builder, expected);
} }
// #[test]
// fn test() {
// let mut builder = LawBuilder::test("UrhG");
//
// builder.new_header("1. Hauptstück");
// builder.new_header("2. Abschnitt");
//
// builder.new_par(Content::Text("Mein erster Paragraph".into()));
//
// let expected = LawBuilder {
// name: "UrhG".into(),
// classifiers: vec![
// Classifier {
// name: "Hauptstück".into(),
// parent: None,
// instances: vec![ClassifierInstance {
// name: "1. Hauptstück".into(),
// desc: None,
// content: vec![],
// }],
// },
// Classifier {
// name: "Abschnitt".into(),
// parent: Some(Box::new(Classifier {
// name: "Hauptstück".into(),
// parent: None,
// instances: vec![],
// })),
// instances: vec![ClassifierInstance {
// name: "2. Abschnitt".into(),
// desc: None,
// content: vec![Content::Text("Mein erster Paragraph".into())],
// }],
// },
// ],
// next_para_header: None,
// last_header_index: Some(1),
// };
// assert_eq!(builder, expected);
// }
} }

View File

@ -42,8 +42,8 @@ impl From<roxmltree::Error> for Error {
fn main() { fn main() {
env_logger::init(); env_logger::init();
let builder = LawBuilder::new("UrhG"); let law = LawBuilder::new("UrhG");
println!("{:#?}", builder); println!("{:#?}", law);
println!("{:#?}", builder.toc()); //println!("{:#?}", builder.toc());
} }

View File

@ -1,6 +1,7 @@
/// This module contains everything everything, to convert the given JSON file into Rust structs using serde. /// This module contains everything everything, to convert the given JSON file into Rust structs using serde.
mod parser; mod parser;
use log::info;
use serde::Deserialize; use serde::Deserialize;
use time::{format_description, OffsetDateTime}; use time::{format_description, OffsetDateTime};
@ -39,12 +40,21 @@ pub(crate) struct Wrapper {
pub(crate) fn parse(overview_id: usize, builder: &mut LawBuilder) -> Result<(), Error> { pub(crate) fn parse(overview_id: usize, builder: &mut LawBuilder) -> Result<(), Error> {
let mut page = 1; let mut page = 1;
let mut skip = true;
loop { loop {
info!("=== Fetching overview page #{page} ===");
let json = fetch_page(overview_id, page)?; let json = fetch_page(overview_id, page)?;
let wrapper: Wrapper = serde_json::from_str(&json)?; let wrapper: Wrapper = serde_json::from_str(&json)?;
for par in wrapper.ogd_search_result.get_par().into_iter().skip(1) { let mut iter = wrapper.ogd_search_result.get_par().into_iter();
let mut boxed_iter: Box<dyn Iterator<Item = String>> = if skip {
skip = false;
Box::new(iter.skip(1)) // You must specify how many items to skip with `n`
} else {
Box::new(iter)
};
for par in boxed_iter {
// skip bc. first one is // skip bc. first one is
// always not relevant for // always not relevant for
// me :-) // me :-)

View File

@ -25,12 +25,27 @@ pub(crate) fn parse(url: &str, builder: &mut LawBuilder) -> Result<bool, Error>
r#"<ueberschrift typ="g1" ct="text" halign="c">1. Verwertungsrechte.</ueberschrift>"#, r#"<ueberschrift typ="g1" ct="text" halign="c">1. Verwertungsrechte.</ueberschrift>"#,
); // 1. Verwertungsrechte. before § 14 ); // 1. Verwertungsrechte. before § 14
let xml = xml.replace(
r#"<ueberschrift typ="para" ct="text" halign="c">4b. Presseveröffentlichungen.</ueberschrift>"#,
r#"<ueberschrift typ="g1" ct="text" halign="c">4b. Presseveröffentlichungen.</ueberschrift>"#,
); // § 99d UrhG, Titel kein Para.... //TODO: not working
let xml = xml.replace("<i>.</i>", "."); // e.g. § 37d Abs. 4 (last point)... let xml = xml.replace("<i>.</i>", "."); // e.g. § 37d Abs. 4 (last point)...
let xml = xml.replace("<i>. </i>", "."); // e.g. § 23a in MSchG
let xml = xml.replace("<super>", ""); // e.g. § 23a in MSchG
let xml = xml.replace("</super>", ""); // e.g. § 23a in MSchG
let xml = xml.replace("<i>", ""); // § 69 in MSchG
let xml = xml.replace("</i>", "");
// Artikel 18 UrhG // Artikel 18 UrhG
let xml = xml.replace("<n><i>", ""); let xml = xml.replace("<n><i>", "");
let xml = xml.replace("</i></n>", ""); let xml = xml.replace("</i></n>", "");
let xml = xml.replace(
r#"(Anm.: § 69 aufgehoben durch Art. 1 Z 12, BGBl. I Nr. 124/2017)"#,
r#"<gldsym>§ 69.</gldsym>(Anm.: § 69 aufgehoben durch Art. 1 Z 12, BGBl. I Nr. 124/2017)"#,
);
debug!("{xml}"); debug!("{xml}");
let continue_parsing = Risdok::from_str(&xml, builder)?; let continue_parsing = Risdok::from_str(&xml, builder)?;

View File

@ -588,63 +588,63 @@ impl Layoutdaten {
} }
} }
#[cfg(test)] //#[cfg(test)]
mod tests { //mod tests {
use std::{fs::File, io::Read}; // use std::{fs::File, io::Read};
//
use log::error; // use log::error;
//
use super::*; // use super::*;
//
#[test] // #[test]
fn deserialize_wucher1_success() { // fn deserialize_wucher1_success() {
let mut file = File::open("data/par/wucher1.xml").unwrap(); // let mut file = File::open("data/par/wucher1.xml").unwrap();
let mut xml = String::new(); // let mut xml = String::new();
file.read_to_string(&mut xml).unwrap(); // file.read_to_string(&mut xml).unwrap();
//
let mut builder = LawBuilder::test("no-headers"); // let mut builder = LawBuilder::test("no-headers");
let risdok = Risdok::from_str(&xml, &mut builder); // let risdok = Risdok::from_str(&xml, &mut builder);
if risdok.is_err() { // if risdok.is_err() {
error!("{:#?}", risdok.as_ref().err()); // error!("{:#?}", risdok.as_ref().err());
} // }
assert!(risdok.is_ok()); // assert!(risdok.is_ok());
//
let abschnitt = risdok.unwrap().nutzdaten.abschnitt; // let abschnitt = risdok.unwrap().nutzdaten.abschnitt;
//
let expected = vec![ // let expected = vec![
AbsatzAbs { // AbsatzAbs {
gldsym: Some("§ 1.".into()), // gldsym: Some("§ 1.".into()),
content: "Ein Vertrag ist nichtig, wenn jemand den Leichtsinn, die Zwangslage, Verstandesschwäche, Unerfahrenheit oder Gemütsaufregung eines anderen dadurch ausbeutet, daß er sich oder einem Dritten für eine Leistung eine Gegenleistung versprechen oder gewähren läßt, deren Vermögenswert zu dem Werte seiner Leistung in auffallendem Mißverhältnis steht.".into() // content: "Ein Vertrag ist nichtig, wenn jemand den Leichtsinn, die Zwangslage, Verstandesschwäche, Unerfahrenheit oder Gemütsaufregung eines anderen dadurch ausbeutet, daß er sich oder einem Dritten für eine Leistung eine Gegenleistung versprechen oder gewähren läßt, deren Vermögenswert zu dem Werte seiner Leistung in auffallendem Mißverhältnis steht.".into()
} // }
]; // ];
assert_eq!(abschnitt.absatze, expected); // assert_eq!(abschnitt.absatze, expected);
} // }
//
#[test] // #[test]
fn deserialize_wucher2_success() { // fn deserialize_wucher2_success() {
let mut file = File::open("data/par/wucher7.xml").unwrap(); // let mut file = File::open("data/par/wucher7.xml").unwrap();
let mut xml = String::new(); // let mut xml = String::new();
file.read_to_string(&mut xml).unwrap(); // file.read_to_string(&mut xml).unwrap();
let mut builder = LawBuilder::test("no-headers"); // let mut builder = LawBuilder::test("no-headers");
//
let risdok = Risdok::from_str(&xml, &mut builder); // let risdok = Risdok::from_str(&xml, &mut builder);
if risdok.is_err() { // if risdok.is_err() {
println!("{:#?}", risdok.as_ref().err()); // println!("{:#?}", risdok.as_ref().err());
} // }
assert!(risdok.is_ok()); // assert!(risdok.is_ok());
//
let abschnitt = risdok.unwrap().nutzdaten.abschnitt; // let abschnitt = risdok.unwrap().nutzdaten.abschnitt;
//
let expected = vec![ // let expected = vec![
AbsatzAbs { // AbsatzAbs {
gldsym: Some("§ 7.".into()), // gldsym: Some("§ 7.".into()),
content: "(1) Ist ein Vertrag nach den vorstehenden Bestimmungen nichtig, so hat jeder der beiden Teile alles zurückzustellen, was er aus dem nichtigen Geschäfte zu seinem Vorteil erhalten hat. Insbesondere sind Geldzahlungen mit den gesetzlichen Zinsen vom Empfangstage zurückzuerstatten, die übergebenen Sachen zurückzustellen oder deren Wert zur Zeit des Empfanges zu ersetzen, die auf die Sache gemachten notwendigen und nützlichen Verwendungen zu ersetzen und für die Benützung und die Entwertung der Sache in der Zwischenzeit eine angemessene Vergütung zu leisten. Ergibt sich aus der Berechnung der beiderseitigen Ansprüche ein Mehranspruch für einen der Vertragsteile, so haftet hiefür die für den vertragsmäßigen Anspruch erworbene Sicherstellung.".into() // content: "(1) Ist ein Vertrag nach den vorstehenden Bestimmungen nichtig, so hat jeder der beiden Teile alles zurückzustellen, was er aus dem nichtigen Geschäfte zu seinem Vorteil erhalten hat. Insbesondere sind Geldzahlungen mit den gesetzlichen Zinsen vom Empfangstage zurückzuerstatten, die übergebenen Sachen zurückzustellen oder deren Wert zur Zeit des Empfanges zu ersetzen, die auf die Sache gemachten notwendigen und nützlichen Verwendungen zu ersetzen und für die Benützung und die Entwertung der Sache in der Zwischenzeit eine angemessene Vergütung zu leisten. Ergibt sich aus der Berechnung der beiderseitigen Ansprüche ein Mehranspruch für einen der Vertragsteile, so haftet hiefür die für den vertragsmäßigen Anspruch erworbene Sicherstellung.".into()
}, // },
AbsatzAbs { // AbsatzAbs {
gldsym: None, // gldsym: None,
content: "(2) Ist jedoch die Gewährung oder Verlängerung von Kredit nach den vorstehenden Bestimmungen nichtig, so hat der Benachteiligte für den erhaltenen Kreditbetrag vom Empfangstag bis zur Rückzahlung sofern im Vertrag nicht eine geringere Verzinsung vorgesehen ist - Zinsen in der Höhe des Zweifachen des im Zeitpunkt der Schließung des Vertrags geltenden Basiszinssatzes zu vergüten. Er kann für die Rückzahlung des Erhaltenen die im Vertrag vorgesehenen Zahlungsfristen in Anspruch nehmen. Bestimmungen, nach denen der Benachteiligte in besonderen Fällen weitergehende Rechte hat, bleiben unberührt.".into() // content: "(2) Ist jedoch die Gewährung oder Verlängerung von Kredit nach den vorstehenden Bestimmungen nichtig, so hat der Benachteiligte für den erhaltenen Kreditbetrag vom Empfangstag bis zur Rückzahlung sofern im Vertrag nicht eine geringere Verzinsung vorgesehen ist - Zinsen in der Höhe des Zweifachen des im Zeitpunkt der Schließung des Vertrags geltenden Basiszinssatzes zu vergüten. Er kann für die Rückzahlung des Erhaltenen die im Vertrag vorgesehenen Zahlungsfristen in Anspruch nehmen. Bestimmungen, nach denen der Benachteiligte in besonderen Fällen weitergehende Rechte hat, bleiben unberührt.".into()
} // }
]; // ];
assert_eq!(abschnitt.absatze, expected); // assert_eq!(abschnitt.absatze, expected);
} // }
} //}